<template>
  <div class="ai-tiptap-editor">
    <!-- 顶部工具栏 -->
    <div v-if="showToolbar" class="ai-toolbar">
      <!-- 基于配置生成的AI功能按钮（仅在showAIToolbar时显示） -->
      <template v-if="showAIToolbar">
        <el-button
          v-for="config in aiFunctionsConfig"
          :key="config.key"
          :size="(config.buttonProps && config.buttonProps.size) || 'mini'"
          :type="(config.buttonProps && config.buttonProps.type) || 'primary'"
          :icon="config.icon"
          :title="config.description"
          @click="handleAIFunction(config)"
        >
          {{ config.name }}
        </el-button>
      </template>

      <!-- 基础工具栏功能 -->
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          title="撤销 (Ctrl+Z)"
          :disabled="!editor || !editor.can().undo()"
          @click="editor && editor.chain().focus().undo().run()"
        >
          ↶
        </button>
        <button
          type="button"
          class="toolbar-btn-small"
          title="重做 (Ctrl+Y)"
          :disabled="!editor || !editor.can().redo()"
          @click="editor && editor.chain().focus().redo().run()"
        >
          ↷
        </button>
      </div>
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          title="代码块"
          :class="{ 'is-active': editor && editor.isActive('codeBlock') }"
          @click="editor && editor.chain().focus().toggleCodeBlock().run()"
        >
          { }
        </button>
      </div>

      <!-- 浮动工具栏开关 -->
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          :title="enableFloatingToolbar ? '隐藏浮动工具栏' : '显示浮动工具栏'"
          :class="{ 'is-active': enableFloatingToolbar }"
          @click="toggleFloatingToolbar"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <rect x="3" y="3" width="18" height="18" rx="2" />
            <path d="M9 9h6v6H9z" />
          </svg>
        </button>
      </div>

      <!-- 格式功能组 -->
      <div class="toolbar-group">
        <!-- 格式选择按钮 -->
        <button
          type="button"
          class="toolbar-btn-small format-btn"
          :title="getCurrentElementTitle()"
          :class="{
            'is-active': showFormatPopup,
            'toolbar-btn-image-active': isImageSelected
          }"
          @click.stop.prevent="toggleFormatPopup"
          @mousedown.stop.prevent
        >
          <div class="current-element-icon-small">
            <div v-if="currentElementType === 'paragraph'" class="element-paragraph-small">¶</div>
            <div v-else-if="currentElementType.startsWith('heading')" class="element-heading-small">
              {{ 'H' + currentElementType.replace('heading', '') }}
            </div>
            <div v-else-if="currentElementType === 'bulletList'" class="element-bullet-list-small">•</div>
            <div v-else-if="currentElementType === 'orderedList'" class="element-ordered-list-small">1.</div>
            <div v-else-if="currentElementType === 'blockquote'" class="element-blockquote-small">""</div>
            <div v-else-if="currentElementType === 'codeBlock'" class="element-code-block-small">&lt;/&gt;</div>
            <div v-else-if="currentElementType === 'table'" class="element-table-small">⊞</div>
            <div v-else-if="isImageSelected" class="element-image-small">🖼</div>
            <div v-else class="element-paragraph-small">¶</div>
            <span class="format-arrow-small">▼</span>
          </div>
        </button>

        <!-- 文本对齐按钮 -->
        <button
          type="button"
          class="toolbar-btn-small"
          :title="getAlignmentTitle()"
          @click="toggleAlignment"
        >
          <div class="alignment-icon-small">
            <div class="align-line-small" :class="`align-${currentAlignment}`" />
            <div class="align-line-small" :class="`align-${currentAlignment}`" />
            <div class="align-line-small" :class="`align-${currentAlignment}`" />
          </div>
        </button>

        <!-- 列表切换按钮 -->
        <button
          type="button"
          class="toolbar-btn-small"
          :title="getListTitle()"
          :class="{ 'is-active': currentListType !== 'paragraph' }"
          @click="toggleList"
        >
          <div class="list-toggle-icon-small">
            <div v-if="currentListType === 'paragraph'" class="paragraph-icon-small">≡</div>
            <div v-else-if="currentListType === 'bulletList'" class="bullet-list-icon-small">•</div>
            <div v-else-if="currentListType === 'orderedList'" class="ordered-list-icon-small">1.</div>
          </div>
        </button>
      </div>

      <!-- 基本格式组 -->
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          title="粗体 (Ctrl+B)"
          :class="{ 'is-active': editor && editor.isActive('bold') }"
          @click="editor && editor.chain().focus().toggleBold().run()"
        >
          <strong>B</strong>
        </button>
        <button
          type="button"
          class="toolbar-btn-small"
          title="斜体 (Ctrl+I)"
          :class="{ 'is-active': editor && editor.isActive('italic') }"
          @click="editor && editor.chain().focus().toggleItalic().run()"
        >
          <em>I</em>
        </button>
        <button
          type="button"
          class="toolbar-btn-small"
          title="下划线 (Ctrl+U)"
          :class="{ 'is-active': editor && editor.isActive('underline') }"
          @click="editor && editor.chain().focus().toggleUnderline().run()"
        >
          <u>U</u>
        </button>
        <button
          type="button"
          class="toolbar-btn-small"
          title="删除线"
          :class="{ 'is-active': editor && editor.isActive('strike') }"
          @click="editor && editor.chain().focus().toggleStrike().run()"
        >
          <s>S</s>
        </button>
      </div>

      <!-- 高级功能组 -->
      <div class="toolbar-group">
        <!-- 字体大小选择 -->
        <el-select
          v-model="currentFontSize"
          size="mini"
          placeholder="字号"
          style="width: 70px;"
          class="font-size-select-small"
          @change="setFontSize"
        >
          <el-option label="12px" value="12px" />
          <el-option label="14px" value="14px" />
          <el-option label="16px" value="16px" />
          <el-option label="18px" value="18px" />
          <el-option label="20px" value="20px" />
          <el-option label="24px" value="24px" />
          <el-option label="28px" value="28px" />
          <el-option label="32px" value="32px" />
        </el-select>

        <!-- 插入链接 -->
        <button
          type="button"
          class="toolbar-btn-small"
          title="插入链接"
          :class="{ 'is-active': editor && editor.isActive('link') }"
          @click="addLink"
        >
          🔗
        </button>

        <!-- 图片（插入/设置） -->
        <button
          type="button"
          class="toolbar-btn-small"
          :title="isImageSelected ? '图片设置' : '插入图片'"
          :class="{ 'is-active': isImageSelected && showImagePopup }"
          @click.stop.prevent="handleImageButtonClick"
          @mousedown.stop.prevent
        >
          🖼️
        </button>

        <!-- 取消链接 -->
        <button
          v-if="editor && editor.isActive('link')"
          type="button"
          class="toolbar-btn-small"
          title="取消链接"
          @click="removeLink"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
            <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
            <line x1="18" y1="6" x2="6" y2="18" />
          </svg>
        </button>

        <!-- 文字颜色 -->
        <div class="color-picker-wrapper-small">
          <button type="button" class="toolbar-btn-small color-btn-small" title="文字颜色">
            <div class="color-icon-small">
              A
              <div class="color-bar-small" :style="{ backgroundColor: currentTextColor }" />
            </div>
          </button>
          <input
            v-model="currentTextColor"
            type="color"
            class="color-input-small"
            @change="setTextColor"
          >
        </div>

        <!-- 背景颜色 -->
        <div class="color-picker-wrapper-small">
          <button type="button" class="toolbar-btn-small color-btn-small" title="背景颜色">
            <div class="highlight-icon-small">
              <div class="highlight-bg-small" :style="{ backgroundColor: currentHighlightColor }">A</div>
            </div>
          </button>
          <input
            v-model="currentHighlightColor"
            type="color"
            class="color-input-small"
            @change="setHighlightColor"
          >
        </div>
      </div>

      <!-- 表格和图片功能组 -->
      <div v-if="isInTable || isImageSelected" class="toolbar-group">
        <!-- 表格控制按钮（仅在表格中显示） -->
        <button
          v-if="isInTable"
          type="button"
          class="toolbar-btn-small"
          title="表格操作"
          :class="{ 'is-active': showTablePopup }"
          @click.stop.prevent="toggleTablePopup"
          @mousedown.stop.prevent
        >
          ⊞
        </button>

        <!-- 图片预览按钮（仅在选中图片时显示） -->
        <button
          v-if="isImageSelected"
          type="button"
          class="toolbar-btn-small"
          title="预览图片"
          @click.stop.prevent="previewImage"
          @mousedown.stop.prevent
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="m1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
            <circle cx="12" cy="12" r="3" />
          </svg>
        </button>

      </div>

      <!-- 源码模式 & 清理内联样式 -->
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          title="源码模式"
          :class="{ 'is-active': isSourceMode }"
          @click="toggleSourceMode"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <polyline points="16,18 22,12 16,6" />
            <polyline points="8,6 2,12 8,18" />
          </svg>
        </button>

        <button
          type="button"
          class="toolbar-btn-small"
          title="清理所有内联样式 (style属性)"
          @click="clearInlineStyles"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M20 9v11a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9" />
            <path d="M3 6h18" />
            <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
            <line x1="10" y1="11" x2="10" y2="17" />
            <line x1="14" y1="11" x2="14" y2="17" />
          </svg>
        </button>
      </div>

      <!-- 更多选项 -->
      <div class="toolbar-group">
        <button
          type="button"
          class="toolbar-btn-small"
          title="更多选项"
          @click="showMoreOptions"
        >
          ◆
        </button>
      </div>
    </div>
    <transition name="fade">
      <div
        v-if="enableFloatingToolbar && showFloatingToolbar && showToolbar"
        class="floating-toolbar"
        :class="{ 'is-dragging': isDragging }"
        :style="floatingToolbarStyle"
        @mouseenter="onToolbarMouseEnter"
        @mouseleave="onToolbarMouseLeave"
      >
        <!-- 拖拽手柄 -->
        <div
          class="toolbar-drag-handle"
          title="拖拽移动工具栏"
          @mousedown="startDrag"
          @touchstart="startDrag"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="9" cy="12" r="1" />
            <circle cx="9" cy="5" r="1" />
            <circle cx="9" cy="19" r="1" />
            <circle cx="15" cy="12" r="1" />
            <circle cx="15" cy="5" r="1" />
            <circle cx="15" cy="19" r="1" />
          </svg>
        </div>
        <!-- 简化的单行工具栏 -->
        <div class="toolbar-row toolbar-compact">
          <!-- 格式选择按钮 -->
          <button
            type="button"
            class="toolbar-btn toolbar-format-btn"
            :title="getCurrentElementTitle()"
            :class="{
              'is-active': showFormatPopup,
              'toolbar-btn-image-active': isImageSelected
            }"
            @click.stop.prevent="toggleFormatPopup"
            @mousedown.stop.prevent
            @mouseenter="onFormatButtonMouseEnter"
            @mouseleave="onFormatButtonMouseLeave"
          >
            <div class="current-element-icon">
              <!-- 段落 -->
              <div v-if="currentElementType === 'paragraph'" class="element-paragraph">
                ¶
              </div>
              <!-- 标题 H1-H6 -->
              <div v-else-if="currentElementType.startsWith('heading')" class="element-heading">
                {{ 'H' + currentElementType.replace('heading', '') }}
              </div>
              <!-- 无序列表 -->
              <div v-else-if="currentElementType === 'bulletList'" class="element-bullet-list">
                <div class="bullet-dot" />
                <div class="bullet-lines">
                  <div class="bullet-line" />
                  <div class="bullet-line" />
                </div>
              </div>
              <!-- 有序列表 -->
              <div v-else-if="currentElementType === 'orderedList'" class="element-ordered-list">
                <div class="number-text">1</div>
                <div class="number-lines">
                  <div class="number-line" />
                  <div class="number-line" />
                </div>
              </div>
              <!-- 引用 -->
              <div v-else-if="currentElementType === 'blockquote'" class="element-blockquote">
                ""
              </div>
              <!-- 代码块 -->
              <div v-else-if="currentElementType === 'codeBlock'" class="element-code-block">
                &lt;/&gt;
              </div>
              <!-- 表格 -->
              <div v-else-if="currentElementType === 'table'" class="element-table">
                <div class="table-grid">
                  <div class="table-row">
                    <div class="table-cell" />
                    <div class="table-cell" />
                  </div>
                  <div class="table-row">
                    <div class="table-cell" />
                    <div class="table-cell" />
                  </div>
                </div>
              </div>
              <!-- 图片 -->
              <div v-else-if="isImageSelected" class="element-image">
                <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
                  <circle cx="9" cy="9" r="2" />
                  <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
                </svg>
              </div>
              <!-- 默认段落 -->
              <div v-else class="element-paragraph">
                ¶
              </div>
              <!-- 下拉箭头 -->
              <div class="format-arrow">▼</div>
            </div>
          </button>

          <!-- 文本对齐按钮 -->
          <button
            type="button"
            class="toolbar-btn"
            :title="getAlignmentTitle()"
            @click="toggleAlignment"
          >
            <div class="alignment-icon">
              <div class="align-line" :class="`align-${currentAlignment}`" />
              <div class="align-line" :class="`align-${currentAlignment}`" />
              <div class="align-line" :class="`align-${currentAlignment}`" />
            </div>
          </button>

          <!-- 列表切换按钮 -->
          <button
            type="button"
            class="toolbar-btn"
            :title="getListTitle()"
            :class="{ 'is-active': currentListType !== 'paragraph' }"
            @click="toggleList"
          >
            <div class="list-toggle-icon">
              <div v-if="currentListType === 'paragraph'" class="paragraph-icon">
                <div class="paragraph-line" />
                <div class="paragraph-line" />
                <div class="paragraph-line" />
              </div>
              <div v-else-if="currentListType === 'bulletList'" class="bullet-list-icon">
                <div class="bullet-dot" />
                <div class="bullet-lines">
                  <div class="bullet-line" />
                  <div class="bullet-line" />
                </div>
              </div>
              <div v-else-if="currentListType === 'orderedList'" class="ordered-list-icon">
                <div class="number-text">1</div>
                <div class="number-lines">
                  <div class="number-line" />
                  <div class="number-line" />
                </div>
              </div>
            </div>
          </button>

          <!-- 基本格式按钮 -->
          <button
            type="button"
            class="toolbar-btn"
            title="粗体 (Ctrl+B)"
            :class="{ 'is-active': editor && editor.isActive('bold') }"
            @click="editor && editor.chain().focus().toggleBold().run()"
          >
            <strong>B</strong>
          </button>
          <button
            type="button"
            class="toolbar-btn"
            title="斜体 (Ctrl+I)"
            :class="{ 'is-active': editor && editor.isActive('italic') }"
            @click="editor && editor.chain().focus().toggleItalic().run()"
          >
            <em>I</em>
          </button>
          <button
            type="button"
            class="toolbar-btn"
            title="下划线 (Ctrl+U)"
            :class="{ 'is-active': editor && editor.isActive('underline') }"
            @click="editor && editor.chain().focus().toggleUnderline().run()"
          >
            <u>U</u>
          </button>
          <button
            type="button"
            class="toolbar-btn"
            title="删除线"
            :class="{ 'is-active': editor && editor.isActive('strike') }"
            @click="editor && editor.chain().focus().toggleStrike().run()"
          >
            <s>S</s>
          </button>

          <!-- 字体大小选择 -->
          <el-select
            v-model="currentFontSize"
            size="mini"
            placeholder="字体样式"
            style="width: 90px; margin: 0 4px;"
            class="font-size-select"
            @change="setFontSize"
            @focus="onFontSelectFocus"
            @blur="onFontSelectBlur"
            @visible-change="onFontSelectVisibleChange"
          >
            <el-option label="12px" value="12px" />
            <el-option label="14px" value="14px" />
            <el-option label="16px" value="16px" />
            <el-option label="18px" value="18px" />
            <el-option label="20px" value="20px" />
            <el-option label="24px" value="24px" />
            <el-option label="28px" value="28px" />
            <el-option label="32px" value="32px" />
          </el-select>

          <!-- 插入链接 -->
          <button
            type="button"
            class="toolbar-btn"
            title="插入链接"
            :class="{ 'is-active': editor && editor.isActive('link') }"
            @click="addLink"
          >
            🔗
          </button>

          <!-- 取消链接 -->
          <button
            v-if="editor && editor.isActive('link')"
            type="button"
            class="toolbar-btn"
            title="取消链接"
            @click="removeLink"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
              <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
              <line x1="18" y1="6" x2="6" y2="18" />
            </svg>
          </button>

          <!-- 文字颜色 -->
          <div class="color-picker-wrapper">
            <button
              type="button"
              class="toolbar-btn color-btn"
              title="文字颜色"
            >
              <div class="color-icon">
                A
                <div class="color-bar" :style="{ backgroundColor: currentTextColor }" />
              </div>
            </button>
            <input
              v-model="currentTextColor"
              type="color"
              class="color-input"
              @change="setTextColor"
              @focus="onColorPickerFocus"
              @blur="onColorPickerBlur"
            >
          </div>

          <!-- 背景颜色 -->
          <div class="color-picker-wrapper">
            <button
              type="button"
              class="toolbar-btn color-btn"
              title="背景颜色"
            >
              <div class="highlight-icon">
                <div class="highlight-bg" :style="{ backgroundColor: currentHighlightColor }">A</div>
              </div>
            </button>
            <input
              v-model="currentHighlightColor"
              type="color"
              class="color-input"
              @change="setHighlightColor"
              @focus="onHighlightPickerFocus"
              @blur="onHighlightPickerBlur"
            >
          </div>

          <!-- 表格控制按钮（仅在表格中显示） -->
          <button
            v-if="isInTable"
            type="button"
            class="toolbar-btn"
            title="表格操作"
            :class="{ 'is-active': showTablePopup }"
            @click.stop.prevent="toggleTablePopup"
            @mousedown.stop.prevent
            @mouseenter="onTableButtonMouseEnter"
            @mouseleave="onTableButtonMouseLeave"
          >
            <div class="table-icon">
              <div class="table-grid">
                <div class="table-row">
                  <div class="table-cell" />
                  <div class="table-cell" />
                </div>
                <div class="table-row">
                  <div class="table-cell" />
                  <div class="table-cell" />
                </div>
              </div>
            </div>
          </button>

          <!-- 图片预览按钮（仅在选中图片时显示） -->
          <button
            v-if="isImageSelected"
            type="button"
            class="toolbar-btn toolbar-btn-image-active"
            title="预览图片"
            @click.stop.prevent="previewImage"
            @mousedown.stop.prevent
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="m1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
              <circle cx="12" cy="12" r="3" />
            </svg>
          </button>

          <!-- 图片控制按钮（仅在选中图片时显示） -->
          <button
            v-if="isImageSelected"
            type="button"
            class="toolbar-btn toolbar-btn-image-active"
            title="图片设置"
            :class="{ 'is-active': showImagePopup }"
            @click.stop.prevent="toggleImagePopup"
            @mousedown.stop.prevent
            @mouseenter="onImageButtonMouseEnter"
            @mouseleave="onImageButtonMouseLeave"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
              <circle cx="9" cy="9" r="2" />
              <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
            </svg>
          </button>

          <!-- 更多选项 -->
          <button
            type="button"
            class="toolbar-btn"
            title="更多选项"
            @click="showMoreOptions"
          >
            <div class="more-icon">◆</div>
          </button>
        </div>
      </div>
    </transition>

    <!-- 格式选择弹窗 -->
    <transition name="fade">
      <div
        v-if="showFormatPopup"
        class="format-popup"
        :style="formatPopupStyle"
        @mousedown.prevent
        @mouseenter="onFormatPopupMouseEnter"
        @mouseleave="onFormatPopupMouseLeave"
      >
        <!-- 标题 -->
        <div class="popup-title">转换为</div>

        <!-- 第一行：段落和标题按钮 -->
        <div class="popup-row">
          <!-- 段落 -->
          <button
            type="button"
            class="popup-btn"
            title="段落"
            :class="{ 'is-active': currentHeading === 'paragraph' }"
            @click.stop.prevent="setHeading('paragraph', $event)"
          >
            ¶
          </button>

          <!-- 标题按钮 H1-H6 -->
          <button
            type="button"
            class="popup-btn"
            title="标题1"
            :class="{ 'is-active': currentHeading === '1' }"
            @click.stop.prevent="setHeading('1')"
          >
            H1
          </button>
          <button
            type="button"
            class="popup-btn"
            title="标题2"
            :class="{ 'is-active': currentHeading === '2' }"
            @click.stop.prevent="setHeading('2')"
          >
            H2
          </button>
          <button
            type="button"
            class="popup-btn"
            title="标题3"
            :class="{ 'is-active': currentHeading === '3' }"
            @click.stop.prevent="setHeading('3')"
          >
            H3
          </button>
          <button
            type="button"
            class="popup-btn"
            title="标题4"
            :class="{ 'is-active': currentHeading === '4' }"
            @click.stop.prevent="setHeading('4')"
          >
            H4
          </button>
          <button
            type="button"
            class="popup-btn"
            title="标题5"
            :class="{ 'is-active': currentHeading === '5' }"
            @click.stop.prevent="setHeading('5')"
          >
            H5
          </button>
          <button
            type="button"
            class="popup-btn"
            title="标题6"
            :class="{ 'is-active': currentHeading === '6' }"
            @click.stop.prevent="setHeading('6')"
          >
            H6
          </button>
        </div>

        <!-- 第二行：列表、引用、代码 -->
        <div class="popup-row">
          <!-- 无序列表 -->
          <button
            type="button"
            class="popup-btn"
            title="无序列表"
            :class="{ 'is-active': editor && editor.isActive('bulletList') }"
            @click.stop.prevent="toggleBulletList($event)"
          >
            <div class="list-bullet-icon">
              <div class="bullet-dot" />
              <div class="bullet-lines">
                <div class="bullet-line" />
                <div class="bullet-line" />
                <div class="bullet-line" />
              </div>
            </div>
          </button>

          <!-- 有序列表 -->
          <button
            type="button"
            class="popup-btn"
            title="有序列表"
            :class="{ 'is-active': editor && editor.isActive('orderedList') }"
            @click.stop.prevent="toggleOrderedList($event)"
          >
            <div class="list-number-icon">
              <div class="number-text">1</div>
              <div class="number-lines">
                <div class="number-line" />
                <div class="number-line" />
                <div class="number-line" />
              </div>
            </div>
          </button>

          <!-- 引用 -->
          <button
            type="button"
            class="popup-btn"
            title="引用"
            :class="{ 'is-active': editor && editor.isActive('blockquote') }"
            @click.stop.prevent="toggleBlockquote($event)"
          >
            ""
          </button>

          <!-- 代码块 -->
          <button
            type="button"
            class="popup-btn"
            title="代码块"
            :class="{ 'is-active': editor && editor.isActive('codeBlock') }"
            @click.stop.prevent="toggleCodeBlock($event)"
          >
            &lt;/&gt;
          </button>
        </div>
      </div>
    </transition>

    <!-- 表格操作弹窗 -->
    <transition name="fade">
      <div
        v-if="showTablePopup"
        class="table-popup"
        :style="tablePopupStyle"
        @mousedown.prevent
        @mouseenter="onTablePopupMouseEnter"
        @mouseleave="onTablePopupMouseLeave"
      >
        <!-- 标题 -->
        <div class="popup-title">表格操作</div>

        <!-- 第一行：表头控制 -->
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="切换表头行"
            @click.stop.prevent="toggleHeaderRow"
          >
            📋 切换表头行
          </button>
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="切换表头列"
            @click.stop.prevent="toggleHeaderColumn"
          >
            📊 切换表头列
          </button>
        </div>

        <!-- 第二行：行操作 -->
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="在上方插入行"
            @click.stop.prevent="addRowBefore"
          >
            ⬆️ 在上方插入行
          </button>
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="在下方插入行"
            @click.stop.prevent="addRowAfter"
          >
            ⬇️ 在下方插入行
          </button>
        </div>

        <!-- 第三行：列操作 -->
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="在左侧插入列"
            @click.stop.prevent="addColumnBefore"
          >
            ⬅️ 在左侧插入列
          </button>
          <button
            type="button"
            class="popup-btn popup-btn-wide"
            title="在右侧插入列"
            @click.stop.prevent="addColumnAfter"
          >
            ➡️ 在右侧插入列
          </button>
        </div>

        <!-- 第四行：删除操作 -->
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-wide popup-btn-danger"
            title="删除当前行"
            @click.stop.prevent="deleteRow"
          >
            🗑️ 删除当前行
          </button>
          <button
            type="button"
            class="popup-btn popup-btn-wide popup-btn-danger"
            title="删除当前列"
            @click.stop.prevent="deleteColumn"
          >
            🗑️ 删除当前列
          </button>
        </div>

        <!-- 第五行：表格操作 -->
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-full popup-btn-danger"
            title="删除整个表格"
            @click.stop.prevent="deleteTable"
          >
            💥 删除整个表格
          </button>
        </div>
      </div>
    </transition>

    <!-- 图片弹窗 -->
    <transition name="popup-fade">
      <div
        v-if="showImagePopup"
        class="image-popup"
        :style="imagePopupStyle"
        @mousedown.prevent
        @mouseenter="onImagePopupMouseEnter"
        @mouseleave="onImagePopupMouseLeave"
      >
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn"
            title="替换图片链接"
            @click.stop.prevent="editImageUrl"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
              <circle cx="9" cy="9" r="2" />
              <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
            </svg>
            替换图片
          </button>
          <button
            type="button"
            class="popup-btn"
            title="编辑图片属性"
            @click.stop.prevent="editImageProperties"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <circle cx="12" cy="12" r="3" />
              <path d="M12 1v6M12 17v6M4.22 4.22l4.24 4.24M15.54 15.54l4.24 4.24M1 12h6M17 12h6M4.22 19.78l4.24-4.24M15.54 8.46l4.24-4.24" />
            </svg>
            图片设置
          </button>
        </div>
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn"
            :title="selectedImageHref ? '编辑图片链接' : '添加图片链接'"
            :class="{ 'is-active': !!selectedImageHref }"
            @click.stop.prevent="toggleImageLink"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
              <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
            </svg>
            {{ selectedImageHref ? '编辑链接' : '添加链接' }}
          </button>
          <button
            v-if="selectedImageHref"
            type="button"
            class="popup-btn popup-btn-danger"
            title="删除图片链接"
            @click.stop.prevent="removeImageLink"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="m18 6-12 12" />
              <path d="m6 6 12 12" />
            </svg>
            删除链接
          </button>
        </div>
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn"
            title="向左对齐"
            @click.stop.prevent="alignImageLeft"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <line x1="21" x2="3" y1="6" y2="6" />
              <line x1="15" x2="3" y1="12" y2="12" />
              <line x1="17" x2="3" y1="18" y2="18" />
            </svg>
            左对齐
          </button>
          <button
            type="button"
            class="popup-btn"
            title="居中对齐"
            @click.stop.prevent="alignImageCenter"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <line x1="21" x2="3" y1="6" y2="6" />
              <line x1="17" x2="7" y1="12" y2="12" />
              <line x1="19" x2="5" y1="18" y2="18" />
            </svg>
            居中
          </button>
          <button
            type="button"
            class="popup-btn"
            title="向右对齐"
            @click.stop.prevent="alignImageRight"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <line x1="21" x2="3" y1="6" y2="6" />
              <line x1="21" x2="9" y1="12" y2="12" />
              <line x1="21" x2="7" y1="18" y2="18" />
            </svg>
            右对齐
          </button>
        </div>
        <div class="popup-row">
          <button
            type="button"
            class="popup-btn popup-btn-danger"
            title="删除图片"
            @click.stop.prevent="deleteImage"
          >
            <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M3 6h18" />
              <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
              <path d="M8 6V4c0-1 1-2 2-2h4c-1 0 2 1 2 2v2" />
            </svg>
            删除图片
          </button>
        </div>
      </div>
    </transition>

    <!-- 编辑器内容 -->
    <div class="tiptap-content" :style="{ minHeight: height + 'px' }" :class="{ 'is-fullscreen': isFullscreen }">
      <!-- 可视化模式 -->
      <editor-content v-if="!isSourceMode" :editor="editor" />

      <!-- 源码模式 -->
      <el-input
        v-if="isSourceMode"
        v-model="sourceCode"
        type="textarea"
        placeholder="HTML 源码"
        :style="{ minHeight: (height - 20) + 'px' }"
        :autosize="{ minRows: 10 }"
        @input="onSourceCodeChange"
      />

      <!-- 插入菜单组件 -->
      <insert-menu v-if="!isSourceMode && editor" ref="insertMenu" :editor="editor" />
    </div>

    <!-- 右键菜单 -->
    <div
      v-if="showContextMenu"
      class="context-menu"
      :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }"
      @click.stop
    >
      <!-- 基于配置生成的AI功能菜单项 -->
      <div
        v-for="config in aiFunctionsConfig"
        v-if="!config.requiresSelection || hasSelection"
        :key="config.key"
        class="context-menu-item"
        @click="handleAIFunction(config)"
      >
        <i :class="config.icon" />
        {{ config.name }}
      </div>

      <div class="context-menu-divider" />
      <div class="context-menu-item" @click="handleGoogleImageSearch">
        <i class="el-icon-search" />
        Google 搜图
      </div>
      <div class="context-menu-item" @click="handleTranslateContent">
        <i class="el-icon-s-promotion" />
        翻译内容
      </div>
      <div class="context-menu-divider" />
      <div class="context-menu-item" @click="handleDuplicateCheck">
        <i class="el-icon-search" />
        重复内容检测
      </div>
      <div class="context-menu-item" @click="handleContentQualityCheck">
        <i class="el-icon-warning" />
        EEAT质量检查
      </div>
      <div class="context-menu-item" @click="clearEmptyParagraphs">
        <i class="el-icon-delete" />
        清理空行
      </div>
      <div v-if="hasAIImagePermission" class="context-menu-item" @click="createAIImage">
        <i class="el-icon-magic-stick" />
        AI 插图
      </div>
    </div>

    <!-- AI 助手组件 -->
    <aicontent
      :id="editorId"
      ref="aiAssistant"
      :app="aiApp"
      :replace_data="replaceData"
      @successCBK="handleAISuccess"
    />

    <!-- AI 分析结果对话框 -->
    <el-drawer
      title="AI 分析结果"
      :visible.sync="analysisVisible"
      direction="rtl"
      size="500px"
    >
      <div style="margin: 10px;">
        <template v-if="analysisResults.length">
          <div v-for="(result, index) in analysisResults" :key="index" style="margin-bottom: 16px;">
            <div style="background: #f5f7fa; padding: 12px; border-radius: 4px; margin-bottom: 8px;">
              <div style="margin-bottom: 8px;">
                <strong>位置：</strong>
                <span>{{ result.position }}</span>
              </div>
              <div style="margin-bottom: 8px;">
                <strong>相似度：</strong>
                <span>{{ result.similarity }}%</span>
              </div>
              <div style="margin-bottom: 8px;">
                <strong>原文：</strong>
                <div style="padding: 8px; background: #fff; border-radius: 4px;">{{ result.original }}</div>
              </div>
              <div style="margin-bottom: 8px;">
                <strong>重复内容：</strong>
                <div style="padding: 8px; background: #fff; border-radius: 4px;">{{ result.duplicate }}</div>
              </div>
              <div>
                <strong>改写建议：</strong>
                <div style="padding: 8px; background: #fff; border-radius: 4px;">{{ result.suggestion }}</div>
              </div>
            </div>
          </div>
        </template>
        <template v-else-if="analysisHtml">
          <div v-html="analysisHtml" />
        </template>
        <template v-else>
          <div style="text-align: center; color: #909399;">
            未发现相关内容
          </div>
        </template>
      </div>
    </el-drawer>

    <!-- 翻译对话框 -->
    <el-drawer
      title="内容翻译"
      :visible.sync="translateVisible"
      direction="rtl"
      size="500px"
    >
      <div style="margin: 10px">
        <el-form :model="translateForm">
          <el-form-item label="目标语言">
            <el-select v-model="translateForm.targetLang" placeholder="选择语言" @change="doTranslate">
              <el-option label="English" value="en" />
              <el-option label="中文" value="zh" />
              <el-option label="日本語" value="ja" />
              <el-option label="한국어" value="ko" />
              <el-option label="Español" value="es" />
              <el-option label="Français" value="fr" />
              <el-option label="Deutsch" value="de" />
            </el-select>
          </el-form-item>

          <el-form-item label="原文">
            <el-input v-model="translateForm.original" type="textarea" readonly rows="4" />
          </el-form-item>

          <el-form-item label="译文">
            <el-input v-model="translateForm.translated" type="textarea" readonly rows="4" />
          </el-form-item>

          <el-form-item>
            <el-button type="primary" :loading="translating" @click="doTranslate">
              翻译
            </el-button>
            <el-button type="success" :disabled="!translateForm.translated" @click="insertTranslation">
              插入翻译
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </el-drawer>

    <!-- 图片编辑对话框 -->
    <el-dialog
      title="编辑图片"
      :visible.sync="imageEditVisible"
      width="500px"
      append-to-body
    >
      <el-form :model="imageEditForm" label-width="80px">
        <el-form-item label="图片链接">
          <el-input
            v-model="imageEditForm.src"
            placeholder="输入图片URL"
            clearable
          />
        </el-form-item>
        <el-form-item label="替代文本">
          <el-input
            v-model="imageEditForm.alt"
            placeholder="图片描述（用于无障碍访问）"
            clearable
          />
        </el-form-item>
        <el-form-item label="图片标题">
          <el-input
            v-model="imageEditForm.title"
            placeholder="鼠标悬停时显示的标题"
            clearable
          />
        </el-form-item>
        <el-form-item label="宽度">
          <el-input
            v-model="imageEditForm.width"
            placeholder="例如：300px 或 50%"
            clearable
          />
        </el-form-item>
        <el-form-item label="高度">
          <el-input
            v-model="imageEditForm.height"
            placeholder="例如：200px 或 auto"
            clearable
          />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="imageEditVisible = false">取消</el-button>
        <el-button type="primary" @click="saveImageProperties">保存</el-button>
      </div>
    </el-dialog>

    <!-- 图片预览对话框 -->
    <el-dialog
      title="图片预览"
      :visible.sync="showImagePreviewDialog"
      width="80%"
      append-to-body
      custom-class="image-preview-dialog"
    >
      <div class="image-preview-container">
        <img
          :src="previewImageUrl"
          :alt="selectedImageAlt"
          class="preview-image"
          @error="handleImageError"
        >
      </div>
      <div slot="footer">
        <el-button @click="showImagePreviewDialog = false">关闭</el-button>
        <el-button type="primary" @click="downloadImage">下载图片</el-button>
      </div>
    </el-dialog>

    <!-- 链接编辑对话框 -->
    <LinkEditDialog
      :visible.sync="linkDialogVisible"
      :editor="editor"
      :current-link="currentLinkData"
    />
  </div>
</template>

<script>
import { Editor, EditorContent, Extension } from '@tiptap/vue-2'
import { Plugin, PluginKey } from 'prosemirror-state'
import StarterKit from '@tiptap/starter-kit'
import EnhancedLink from './extensions/EnhancedLink.js'
import Underline from '@tiptap/extension-underline'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Color from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableHeader from '@tiptap/extension-table-header'
import TableCell from '@tiptap/extension-table-cell'
import Placeholder from '@tiptap/extension-placeholder'
import Gapcursor from '@tiptap/extension-gapcursor'
import Aicontent from '../aicontent'
import InsertMenu from './InsertMenu.vue'
import VideoNode from './extensions/VideoNode.js'
import CustomImage from './extensions/CustomImage.js'
import LinkEditDialog from './LinkEditDialog.vue'
import { Post as GetAiContent, GenerateImage } from '@/api/cms/openai'
import { Post as UploadBase64Image } from '@/api/cms/image'
import { AI_FUNCTIONS_CONFIG, getPromptConfig, AI_OPERATION_TYPES } from './ai-functions-config.js'

export default {
  name: 'AITiptap',
  components: {
    EditorContent,
    Aicontent,
    InsertMenu,
    LinkEditDialog
  },
  props: {
    value: {
      type: String,
      default: ''
    },
    height: {
      type: Number,
      default: 300
    },
    showToolbar: {
      type: Boolean,
      default: true
    },
    showAIToolbar: {
      type: Boolean,
      default: false
    },
    aiApp: {
      type: String,
      default: 'content'
    },
    onSave: {
      type: Function,
      default: null
    }
  },
  data() {
    return {
      editor: null,
      editorId: 'ai-tiptap-' + Date.now(),
      replaceData: {},
      analysisVisible: false,
      analysisResults: [],
      analysisHtml: '',
      showContextMenu: false,
      contextMenuX: 0,
      contextMenuY: 0,
      translateVisible: false,
      translating: false,
      translateForm: {
        targetLang: '',
        original: '',
        translated: ''
      },
      isSourceMode: false,
      sourceCode: '',
      isFullscreen: false,
      currentHeading: 'paragraph',
      currentTextColor: '#000000',
      currentHighlightColor: '#ffff00',
      showFloatingToolbar: false,
      enableFloatingToolbar: false, // 默认隐藏浮动工具栏
      floatingToolbarStyle: {
        top: '0px',
        left: '0px'
      },
      toolbarTimeout: null,
      // 拖拽相关状态
      isDragging: false,
      dragStartX: 0,
      dragStartY: 0,
      toolbarDragOffset: { x: 0, y: 0 },
      showFormatPopup: false,
      formatPopupStyle: {
        top: '0px',
        left: '0px'
      },
      currentFontSize: '14px',
      fontSelectActive: false,
      colorPickerActive: false,
      highlightPickerActive: false,
      currentAlignment: 'left',
      // AI 状态管理 - 重新设计
      aiTasks: new Map(), // 存储AI任务：id -> { type, status, originalContent }
      aiIdCounter: 0,
      formatPopupActive: false,
      currentListType: 'paragraph', // 'paragraph', 'bulletList', 'orderedList'
      isInTable: false,
      showTablePopup: false,
      tablePopupStyle: {
        top: '0px',
        left: '0px'
      },
      tablePopupActive: false,
      currentElementType: 'paragraph', // 'paragraph', 'heading1-6', 'bulletList', 'orderedList', 'blockquote', 'codeBlock', 'table'
      // 图片相关状态
      isImageSelected: false,
      selectedImageUrl: '',
      selectedImageAlt: '',
      selectedImageHref: '',
      selectedImageTarget: '',
      showImagePopup: false,
      imagePopupStyle: {
        top: '0px',
        left: '0px'
      },
      imagePopupActive: false,
      // 图片编辑对话框
      imageEditVisible: false,
      imageEditForm: {
        src: '',
        alt: '',
        title: '',
        width: '',
        height: ''
      },
      // 图片预览对话框
      imagePreviewVisible: false,
      showImagePreviewDialog: false,
      previewImageUrl: '',
      // AI功能配置
      aiFunctionsConfig: AI_FUNCTIONS_CONFIG,
      // 链接编辑对话框
      linkDialogVisible: false,
      currentLinkData: {}
    }
  },
  computed: {
    hasAIImagePermission() {
      return this.$store.getters.roles.indexOf('ai_image_user') !== -1
    },
    hasSelection() {
      if (!this.editor) return false
      const { from, to } = this.editor.state.selection
      return from !== to
    }
  },
  watch: {
    value(val) {
      if (!this.editor) return
      const isSame = this.editor.getHTML() === val
      if (isSame) return
      this.editor.commands.setContent(val, false)
    }
  },
  mounted() {
    this.initEditor()
    this.bindGlobalEvents()
  },
  beforeDestroy() {
    if (this.editor) {
      this.editor.destroy()
    }
    this.unbindGlobalEvents()

    // 清理定时器
    if (this.toolbarTimeout) {
      clearTimeout(this.toolbarTimeout)
    }

    // 清理拖拽事件监听器
    if (this.isDragging) {
      document.removeEventListener('mousemove', this.handleDrag)
      document.removeEventListener('mouseup', this.endDrag)
      document.removeEventListener('touchmove', this.handleDrag)
      document.removeEventListener('touchend', this.endDrag)
      // 恢复样式
      document.body.style.userSelect = ''
      document.body.style.cursor = ''
    }

    // 清理全屏状态
    if (this.isFullscreen) {
      document.body.style.overflow = ''
    }
  },
  methods: {
    // =========================== 配置化AI功能处理方法 ===========================

    /**
     * 统一的AI功能处理入口
     * @param {Object} config - AI功能配置对象
     */
    async handleAIFunction(config) {
      console.log('执行AI功能:', config.name, config)

      // 验证功能是否可用
      if (!this.validateAIFunction(config)) {
        return
      }

      try {
        switch (config.type) {
          case AI_OPERATION_TYPES.INSERT:
            await this.handleInsertAIFunction(config)
            break
          case AI_OPERATION_TYPES.REPLACE:
            await this.handleReplaceAIFunction(config)
            break
          case AI_OPERATION_TYPES.PROCESS:
            await this.handleProcessAIFunction(config)
            break
          default:
            console.error('未知的AI操作类型:', config.type)
            this.$message.error('未知的AI操作类型')
        }
      } catch (error) {
        console.error('AI功能执行错误:', error)
        this.$message.error(`执行${config.name}时发生错误: ${error.message}`)
      }
    },

    /**
     * 验证AI功能是否可执行
     * @param {Object} config - AI功能配置
     * @returns {boolean}
     */
    validateAIFunction(config) {
      // 检查是否需要选中内容
      if (config.requiresSelection && !this.hasSelection) {
        this.$message.warning(`${config.name}需要先选中内容`)
        return false
      }

      // 检查编辑器状态
      if (!this.editor) {
        this.$message.error('编辑器未初始化')
        return false
      }

      return true
    },

    /**
     * 处理插入类型的AI功能
     * @param {Object} config - AI功能配置
     */
    async handleInsertAIFunction(config) {
      console.log('处理插入类型AI功能:', config.name)

      // 先获取当前内容（在插入占位符之前）
      const currentContent = this.editor.getText()
      console.log('当前文档内容:', currentContent)

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder(config.key)

      let insertPosition

      // 根据功能类型确定插入位置
      if (config.key === 'start') {
        // AI开头：插入到文档最开始
        insertPosition = 0
        console.log('AI开头功能，插入到文档开头位置：', insertPosition)
      } else if (config.key === 'end') {
        // AI结尾：插入到文档末尾
        insertPosition = this.editor.state.doc.content.size
        console.log('AI结尾功能，插入到文档末尾位置：', insertPosition)
      } else {
        // 其他插入功能：使用当前光标位置
        insertPosition = this.editor.state.selection.from
        console.log('其他插入功能，插入到光标位置：', insertPosition)
      }

      // 插入占位符
      this.editor.chain().focus().insertContentAt(insertPosition, placeholderHtml).run()

      // 执行AI请求，传递预先获取的内容
      await this.executeAIRequest(config, aiId, currentContent)
    },

    /**
     * 处理替换类型的AI功能
     * @param {Object} config - AI功能配置
     */
    async handleReplaceAIFunction(config) {
      console.log('处理替换类型AI功能:', config.name)

      // 获取选中内容
      const selectedContent = this.editor.state.doc.textBetween(
        this.editor.state.selection.from,
        this.editor.state.selection.to,
        '\n'
      )

      if (!selectedContent.trim()) {
        this.$message.warning('请先选中要处理的内容')
        return
      }

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder(config.key, selectedContent)

      // 替换选中内容为占位符
      this.editor.chain().focus().deleteSelection().insertContent(placeholderHtml).run()

      // 执行AI请求
      await this.executeAIRequest(config, aiId, selectedContent)
    },

    /**
     * 处理全文处理类型的AI功能
     * @param {Object} config - AI功能配置
     */
    async handleProcessAIFunction(config) {
      console.log('处理全文处理类型AI功能:', config.name)

      // 获取全文内容
      const textContent = this.editor.getText()

      if (!textContent.trim()) {
        this.$message.warning('文档内容为空')
        return
      }

      // 对于全文处理，我们可以选择不同的处理方式
      if (config.key === 'duplicateCheck') {
        // 重复检测：显示分析结果
        await this.performDuplicateCheck(textContent)
      } else if (config.key === 'aiImageSearch') {
        // AI搜图：显示搜索结果
        await this.performAIImageSearch(textContent)
      } else {
        // 其他全文处理：在末尾插入结果
        const { aiId, placeholderHtml } = this.createAIPlaceholder(config.key)
        this.editor.chain().focus().setTextSelection(this.editor.state.doc.content.size).insertContent('<p><br></p>' + placeholderHtml).run()
        await this.executeAIRequest(config, aiId, textContent)
      }
    },

    /**
     * 执行AI请求
     * @param {Object} config - AI功能配置
     * @param {string} aiId - AI任务ID
     * @param {string} content - 待处理的内容
     */
    async executeAIRequest(config, aiId, content = '') {
      try {
        // 获取提示配置
        const promptConfig = getPromptConfig(config)

        // 构建基础请求参数
        const requestParams = {
          engine: config.engine || 'gpt-4o-mini'
        }

        // 根据提示配置类型设置参数
        if (promptConfig.type === 'direct') {
          // 直接使用prompt
          requestParams.prompt = content ? `${promptConfig.content}\n\n${content}` : promptConfig.content
        } else if (promptConfig.type === 'id') {
          // 使用prompt ID
          requestParams.aiprompt_id = promptConfig.value
          requestParams.prompt = content
        } else if (promptConfig.type === 'name') {
          // 使用prompt名称 (需要后端支持)
          requestParams.prompt_name = promptConfig.value
          requestParams.prompt = content
        }

        // 添加配置中的特殊参数
        if (config.aiprompt_id) {
          requestParams.aiprompt_id = config.aiprompt_id
        }

        if (config.prompt_name) {
          requestParams.prompt_name = config.prompt_name
        }

        if (config.humanway !== undefined) {
          requestParams.humanway = config.humanway
        }

        if (config.keywords) {
          requestParams.keywords = config.keywords
        }

        // 构建kvs参数（如果配置需要）
        if (config.kvs || config.key === 'start') {
          // 使用传递的content参数（预先获取的内容）或当前内容
          const contentToUse = content || this.editor.getText()
          const title = this.getDocumentTitle()

          requestParams.kvs = config.kvs || [
            { 'content': contentToUse, 'title': title }
          ]

          console.log('构建kvs参数:', {
            content: contentToUse,
            title: title,
            contentLength: contentToUse.length
          })
        }

        console.log('发送AI请求:', requestParams)

        // 发送请求
        const response = await GetAiContent(requestParams)

        if (response && response.content) {
          // 替换占位符为生成的内容
          const success = this.replaceAIPlaceholder(aiId, response.content)
          if (success) {
            this.$message.success(`${config.name}完成`)
          } else {
            this.$message.error(`${config.name}失败：无法替换内容`)
          }
        } else {
          this.removeAIPlaceholder(aiId, true)
          this.$message.error(`${config.name}失败：未获取到有效内容`)
        }
      } catch (error) {
        console.error('AI请求失败:', error)
        this.removeAIPlaceholder(aiId, true)
        this.$message.error(`${config.name}失败: ${error.message}`)
      }
    },

    /**
     * 执行重复内容检测
     * @param {string} content - 文档内容
     */
    async performDuplicateCheck(content) {
      try {
        const response = await GetAiContent({
          prompt: `请分析以下文档内容，检测是否存在重复的句子、段落或观点，并提供修改建议：\n\n${content}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          this.analysisResults = [{ content: response.content }]
          this.analysisVisible = true
          this.$message.success('重复检测完成')
        } else {
          this.$message.error('重复检测失败：未获取到分析结果')
        }
      } catch (error) {
        console.error('重复检测失败:', error)
        this.$message.error(`重复检测失败: ${error.message}`)
      }
    },

    /**
     * 执行AI图片搜索
     * @param {string} content - 文档内容
     */
    async performAIImageSearch(content) {
      // 这里可以实现AI图片搜索逻辑
      this.$message.info('AI搜图功能开发中...')
      console.log('AI搜图 - 文档内容:', content)
    },

    /**
     * 获取文档标题
     * @returns {string} 文档标题
     */
    getDocumentTitle() {
      // 尝试从编辑器内容中提取第一个标题
      if (!this.editor) return ''

      const doc = this.editor.state.doc
      let title = ''

      // 遍历文档寻找第一个标题节点
      doc.descendants((node) => {
        if (node.type.name === 'heading' && node.textContent.trim() && !title) {
          title = node.textContent.trim()
          return false // 停止遍历
        }
      })

      // 如果没有找到标题，尝试获取第一个段落的前50个字符作为标题
      if (!title) {
        const firstParagraph = this.editor.getText().split('\n')[0]
        title = firstParagraph ? firstParagraph.substring(0, 50).trim() : '无标题文档'
      }

      return title
    },

    // =========================== 原有的AI状态管理方法 ===========================
    // AI 状态管理方法 - 统一机制

    // 创建AI占位符（通用方法，支持配置化消息）
    createAIPlaceholder(type, originalContent = null, customMessage = null) {
      const aiId = `ai-task-${++this.aiIdCounter}`

      // 获取加载消息
      let loadingMessage = customMessage || 'AI 正在处理...'

      // 如果有配置，使用配置中的消息
      if (!customMessage) {
        const config = this.aiFunctionsConfig.find(c => c.key === type)
        if (config && config.loadingMessage) {
          loadingMessage = config.loadingMessage
        } else {
          // 兜底消息
          const defaultMessages = {
            'start': '🤖 AI 正在生成文章开头...',
            'end': '🤖 AI 正在生成文章结尾...',
            'expertOpinion': '🤖 AI 正在生成专家意见...',
            'rewrite': '🤖 AI 正在重写内容...',
            'expand': '🤖 AI 正在扩写段落...',
            'table': '🤖 AI 正在转换为表格...',
            'firstPerson': '🤖 AI 正在第一人称优化...',
            'techToExample': '🤖 AI 正在转换为案例...',
            'duplicateCheck': '🤖 AI 正在检测重复内容...',
            'aiImageSearch': '🤖 AI 正在搜索相关图片...'
          }
          loadingMessage = defaultMessages[type] || '🤖 AI 正在处理...'
        }
      }

      // 存储任务信息
      this.aiTasks.set(aiId, {
        type: type,
        status: 'generating',
        originalContent: originalContent
      })

      // 创建统一的占位符HTML
      const placeholderHtml = `<p class="ai-placeholder ai-task-${aiId}" data-ai-id="${aiId}" style="padding: 8px 12px; background: #f3f4f6; border: 2px solid #3b82f6; border-radius: 6px; margin: 8px 0; font-size: 14px; font-weight: 500; color: #1f2937;">${loadingMessage} [${aiId}]</p>`

      return { aiId, placeholderHtml }
    },

    // 统一的AI占位符替换方法
    replaceAIPlaceholder(aiId, generatedContent) {
      const task = this.aiTasks.get(aiId)
      if (!task) {
        console.error('AI任务不存在:', aiId)
        return false
      }

      try {
        console.log('======== 开始替换AI占位符 ========')
        console.log('AI ID:', aiId)
        console.log('生成的内容:', generatedContent)

        const currentHTML = this.editor.getHTML()

        // 根据任务类型决定是否添加空段落
        const isInsertType = ['start', 'end', 'expertOpinion'].includes(task.type)
        let finalContent = generatedContent

        if (isInsertType) {
          // 插入型：添加上下空段落
          const emptyParagraph = '<p><br class="ProseMirror-trailingBreak"></p>'
          finalContent = emptyParagraph + generatedContent + emptyParagraph
        }

        // 使用正则表达式精确替换占位符
        const placeholderRegex = new RegExp(
          `<p[^>]*class="[^"]*ai-placeholder[^"]*ai-task-${aiId}[^"]*"[^>]*>.*?</p>`,
          'g'
        )

        const newHTML = currentHTML.replace(placeholderRegex, finalContent)

        if (newHTML !== currentHTML) {
          this.editor.commands.setContent(newHTML)
          console.log('✅ 占位符替换成功')
          this.aiTasks.delete(aiId)

          // 延迟执行清理，确保内容设置完成
          setTimeout(() => {
            this.cleanEmptyElements()
            // 如果是开头类型，额外清理开头的空段落
            if (task.type === 'start') {
              this.cleanLeadingEmptyParagraphs()
            }
          }, 100)

          return true
        } else {
          console.error('❌ 占位符替换失败，未找到匹配的占位符')
          return false
        }
      } catch (error) {
        console.error('❌ 替换AI占位符异常:', error)
        return false
      }
    },

    // 统一的AI占位符移除方法（错误处理）
    removeAIPlaceholder(aiId, restoreOriginal = false) {
      const task = this.aiTasks.get(aiId)
      if (!task) return false

      try {
        console.log('移除AI占位符:', aiId, '恢复原文:', restoreOriginal)

        const currentHTML = this.editor.getHTML()
        let contentToRestore = ''

        if (restoreOriginal && task.originalContent) {
          contentToRestore = task.originalContent
        }

        // 使用正则表达式移除占位符
        const placeholderRegex = new RegExp(
          `<p[^>]*class="[^"]*ai-placeholder[^"]*ai-task-${aiId}[^"]*"[^>]*>.*?</p>`,
          'g'
        )

        const newHTML = currentHTML.replace(placeholderRegex, contentToRestore)

        if (newHTML !== currentHTML) {
          this.editor.commands.setContent(newHTML)
          console.log('✅ 占位符移除成功')
          this.aiTasks.delete(aiId)
          return true
        } else {
          console.error('❌ 占位符移除失败，未找到匹配的占位符')
          this.aiTasks.delete(aiId) // 仍然清理任务记录
          return false
        }
      } catch (error) {
        console.error('移除AI占位符失败:', error)
        this.aiTasks.delete(aiId) // 仍然清理任务记录
        return false
      }
    },

    // 清理文档开头的空段落
    cleanLeadingEmptyParagraphs() {
      try {
        console.log('清理开头的空段落...')
        const currentHTML = this.editor.getHTML()
        console.log('清理前的HTML:', currentHTML)

        // 移除开头的空段落，包括ProseMirror-trailingBreak
        let cleanHTML = currentHTML

        // 多轮清理，直到没有开头的空段落
        let previousHTML = ''
        while (cleanHTML !== previousHTML) {
          previousHTML = cleanHTML
          cleanHTML = cleanHTML
            .replace(/^<p><br[^>]*class="[^"]*ProseMirror-trailingBreak[^"]*"[^>]*><\/p>\s*/, '') // 移除开头的trailingBreak段落
            .replace(/^<p><br[^>]*><\/p>\s*/, '') // 移除其他开头空段落
            .replace(/^<p>\s*<\/p>\s*/, '') // 移除只包含空白的段落
            .replace(/^\s+/, '') // 移除开头的空白字符
        }

        if (cleanHTML !== currentHTML) {
          console.log('清理后的HTML:', cleanHTML)
          this.editor.commands.setContent(cleanHTML)
          console.log('✅ 开头空段落清理完成')
        } else {
          console.log('无需清理，HTML没有变化')
        }
      } catch (error) {
        console.error('清理开头空段落失败:', error)
      }
    },

    // 清理空的HTML元素
    cleanEmptyElements() {
      try {
        console.log('清理空的HTML元素...')
        const currentHTML = this.editor.getHTML()
        console.log('清理前的HTML:', currentHTML)

        let cleanHTML = currentHTML
        let previousHTML = ''

        // 多轮清理，直到没有更多的空元素需要清理
        while (cleanHTML !== previousHTML) {
          previousHTML = cleanHTML
          cleanHTML = cleanHTML
            // 移除空的段落标签
            .replace(/<p><\/p>/g, '')
            // 移除只包含空白字符的段落
            .replace(/<p>\s*<\/p>/g, '')
            // 移除只包含换行符的段落
            .replace(/<p>\s*<br[^>]*>\s*<\/p>/g, '')
            // 移除只包含ProseMirror-trailingBreak的段落
            .replace(/<p>\s*<br[^>]*class="[^"]*ProseMirror-trailingBreak[^"]*"[^>]*>\s*<\/p>/g, '')
            // 移除空的标题标签
            .replace(/<h[1-6]><\/h[1-6]>/g, '')
            .replace(/<h[1-6]>\s*<\/h[1-6]>/g, '')
            // 移除空的div标签
            .replace(/<div><\/div>/g, '')
            .replace(/<div>\s*<\/div>/g, '')
            // 移除空的blockquote标签
            .replace(/<blockquote><\/blockquote>/g, '')
            .replace(/<blockquote>\s*<\/blockquote>/g, '')
            // 移除多余的连续空白字符
            .replace(/\s{2,}/g, ' ')
            // 移除标签之间的多余空白
            .replace(/>\s+</g, '><')
        }

        // 确保文档不完全为空，至少有一个段落
        if (!cleanHTML.trim() || cleanHTML.trim() === '') {
          cleanHTML = '<p><br class="ProseMirror-trailingBreak"></p>'
        }

        if (cleanHTML !== currentHTML) {
          console.log('清理后的HTML:', cleanHTML)
          this.editor.commands.setContent(cleanHTML)
          console.log('✅ 空元素清理完成')
        } else {
          console.log('无需清理，HTML没有变化')
        }
      } catch (error) {
        console.error('清理空元素失败:', error)
      }
    },

    // 🧪 测试方法：验证并发AI操作
    testConcurrentAIOperations() {
      console.log('🧪 开始测试并发AI操作...')

      // 模拟连续创建多个AI任务
      const task1 = this.createReplaceAIMarker('rewrite', '第一段内容')
      const task2 = this.createReplaceAIMarker('rewrite', '第二段内容')
      const task3 = this.createInsertAIMarker('start')
      const task4 = this.createReplaceAIMarker('expand', '第三段内容')
      const task5 = this.createReplaceAIMarker('table', '第四段内容')

      console.log('✅ 生成的任务ID:')
      console.log('Task 1 (rewrite):', task1.aiId)
      console.log('Task 2 (rewrite):', task2.aiId)
      console.log('Task 3 (start):', task3.aiId)
      console.log('Task 4 (expand):', task4.aiId)
      console.log('Task 5 (table):', task5.aiId)

      console.log('✅ AI任务存储状态:')
      console.log('总任务数量:', this.aiTasks.size)
      this.aiTasks.forEach((task, id) => {
        console.log(`${id}:`, task)
      })

      console.log('✅ 验证ID唯一性:')
      const ids = [task1.aiId, task2.aiId, task3.aiId, task4.aiId, task5.aiId]
      const uniqueIds = new Set(ids)
      console.log('所有ID:', ids)
      console.log('唯一ID数量:', uniqueIds.size)
      console.log('ID是否全部唯一:', uniqueIds.size === ids.length ? '✅ 是' : '❌ 否')

      return {
        allTasks: [task1, task2, task3, task4, task5],
        isUnique: uniqueIds.size === ids.length
      }
    },

    initEditor() {
      this.editor = new Editor({
        extensions: [
          StarterKit,
          Gapcursor,
          EnhancedLink.configure({
            openOnClick: false,
            linkOnPaste: true,
            HTMLAttributes: {
              class: 'editor-link'
            }
          }),
          CustomImage,
          VideoNode,
          Underline,
          TextStyle,
          Color,
          Highlight.configure({
            multicolor: true
          }),
          TextAlign.configure({
            types: ['heading', 'paragraph']
          }),
          Table.configure({
            resizable: true
          }),
          TableRow,
          TableHeader,
          TableCell,
          Placeholder.configure({
            placeholder: ({ node }) => {
              // 默认 placeholder
              if (node.type.name === 'heading') {
                return `标题 ${node.attrs.level}`
              }
              if (node.type.name === 'paragraph') {
                return "输入 '/' 显示可选项"
              }
              return '开始输入...'
            }
          }),
          // AI 标记全局属性扩展 - 保留 class 和 style 属性
          Extension.create({
            name: 'aiMarkerGlobalAttributes',
            addGlobalAttributes() {
              return [
                {
                  // 只扩展到支持属性的节点类型，text节点不支持属性
                  types: ['paragraph', 'heading', 'textStyle'],
                  attributes: {
                    class: {
                      default: null,
                      parseHTML: element => element.getAttribute('class'),
                      renderHTML: attributes => {
                        if (!attributes.class) {
                          return {}
                        }
                        return {
                          class: attributes.class
                        }
                      }
                    },
                    style: {
                      default: null,
                      parseHTML: element => element.getAttribute('style'),
                      renderHTML: attributes => {
                        if (!attributes.style) {
                          return {}
                        }
                        return {
                          style: attributes.style
                        }
                      }
                    },
                    contenteditable: {
                      default: null,
                      parseHTML: element => element.getAttribute('contenteditable'),
                      renderHTML: attributes => {
                        if (!attributes.contenteditable) {
                          return {}
                        }
                        return {
                          contenteditable: attributes.contenteditable
                        }
                      }
                    },
                    'data-ai-id': {
                      default: null,
                      parseHTML: element => element.getAttribute('data-ai-id'),
                      renderHTML: attributes => {
                        if (!attributes['data-ai-id']) {
                          return {}
                        }
                        return {
                          'data-ai-id': attributes['data-ai-id']
                        }
                      }
                    }
                  }
                }
              ]
            }
          }),
          // 自定义键盘处理扩展
          Extension.create({
            name: 'insertMenuKeyHandler',
            addKeyboardShortcuts() {
              const that = this
              return {
                Enter: () => {
                  // 检查 InsertMenu 是否显示
                  if (that.editor.storage.insertMenuVisible) {
                    // 让 InsertMenu 处理 Enter 键
                    const insertMenu = that.editor.storage.insertMenuComponent
                    if (insertMenu && insertMenu.showInsertMenu) {
                      insertMenu.selectCurrentItem()
                      return true // 阻止默认行为
                    }
                  }
                  return false // 允许默认行为
                },
                Escape: () => {
                  // 检查 InsertMenu 是否显示
                  if (that.editor.storage.insertMenuVisible) {
                    const insertMenu = that.editor.storage.insertMenuComponent
                    if (insertMenu && insertMenu.showInsertMenu) {
                      insertMenu.hideInsertMenu()
                      return true // 阻止默认行为
                    }
                  }
                  return false
                },
                ArrowUp: () => {
                  if (that.editor.storage.insertMenuVisible) {
                    const insertMenu = that.editor.storage.insertMenuComponent
                    if (insertMenu && insertMenu.showInsertMenu) {
                      insertMenu.navigateUp()
                      return true
                    }
                  }
                  return false
                },
                ArrowDown: () => {
                  if (that.editor.storage.insertMenuVisible) {
                    const insertMenu = that.editor.storage.insertMenuComponent
                    if (insertMenu && insertMenu.showInsertMenu) {
                      insertMenu.navigateDown()
                      return true
                    }
                  }
                  return false
                }
              }
            }
          }),
          // 粘贴图片自动上传扩展
          Extension.create({
            name: 'pasteImageUpload',
            onCreate() {
              const component = this.editor.storage.component
              // 绑定粘贴事件监听器
              this.editor.view.dom.addEventListener('paste', (event) => {
                component.handlePasteEvent(event)
              })

              // 绑定拖拽事件监听器
              this.editor.view.dom.addEventListener('drop', (event) => {
                component.handleDropEvent(event)
              })
            }
          }),
          // 粘贴时自动清理内联样式扩展
          Extension.create({
            name: 'pasteCleanInlineStyles',
            addProseMirrorPlugins() {
              return [
                new Plugin({
                  key: new PluginKey('pasteCleanInlineStyles'),
                  props: {
                    transformPastedHTML(html) {
                      // 创建临时 DOM 元素来处理 HTML
                      const tempDiv = document.createElement('div')
                      tempDiv.innerHTML = html

                      // 递归移除所有 style 属性
                      const removeStyles = (element) => {
                        if (element.hasAttribute && element.hasAttribute('style')) {
                          element.removeAttribute('style')
                        }
                        if (element.children) {
                          Array.from(element.children).forEach(child => {
                            removeStyles(child)
                          })
                        }
                      }

                      removeStyles(tempDiv)
                      return tempDiv.innerHTML
                    }
                  }
                })
              ]
            }
          })
        ],
        content: this.value,
        onUpdate: () => {
          this.$emit('input', this.editor.getHTML())
          this.updateCurrentHeading()
          this.updateCurrentListType()
          this.updateTableState()
          this.updateCurrentElementType()
          this.checkForSlashKeyword()
        },
        onSelectionUpdate: () => {
          this.updateReplaceData()
          this.updateCurrentHeading()
          this.updateCurrentListType()
          this.updateTableState()
          this.updateCurrentElementType()
          this.updateImageState()
          this.updateFloatingToolbar()

          // 检查选区是否为空,如果为空则通知父组件清空选区
          if (this.editor) {
            const { from, to } = this.editor.state.selection
            if (from === to) {
              // 选区为空,通知父组件清空
              this.$emit('selection-cleared')
            }
          }
        },
        onFocus: () => {
          this.updateFloatingToolbar()
        },
        onBlur: () => {
          this.scheduleHideToolbar()
        },
        onCreate: ({ editor }) => {
          // 添加链接点击事件处理
          editor.view.dom.addEventListener('click', (event) => {
            const linkElement = event.target.closest('a')
            if (linkElement && event.ctrlKey) {
              // Ctrl+点击编辑链接
              event.preventDefault()
              const pos = editor.view.posAtDOM(linkElement, 0)
              if (pos) {
                editor.commands.setTextSelection(pos)
                this.editCurrentLink()
              }
            }
          })
        }
      })

      // 初始化编辑器 storage
      if (this.editor) {
        this.editor.storage.insertMenuVisible = false
        this.editor.storage.insertMenuComponent = null
        this.editor.storage.component = this // 添加组件引用
      }
    },

    bindGlobalEvents() {
      document.addEventListener('click', this.hideContextMenu)
      document.addEventListener('contextmenu', this.handleContextMenu)
      document.addEventListener('keydown', this.handleKeyDown)
    },

    unbindGlobalEvents() {
      document.removeEventListener('click', this.hideContextMenu)
      document.removeEventListener('contextmenu', this.handleContextMenu)
      document.removeEventListener('keydown', this.handleKeyDown)
    },

    handleContextMenu(event) {
      const editorElement = this.$el.querySelector('.ProseMirror')
      if (!editorElement || !editorElement.contains(event.target)) {
        return
      }

      event.preventDefault()
      this.contextMenuX = event.clientX
      this.contextMenuY = event.clientY
      this.showContextMenu = true
    },

    hideContextMenu() {
      this.showContextMenu = false
    },

    handleKeyDown(event) {
      // 检查是否是编辑器区域内的按键事件
      const editorElement = this.$el.querySelector('.tiptap-content')
      if (!editorElement || !editorElement.contains(event.target)) {
        return
      }

      // 检测保存快捷键 (Ctrl+S 或 Cmd+S)
      const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
      const saveKey = isMac ? event.metaKey : event.ctrlKey

      if (saveKey && event.key === 's') {
        event.preventDefault()
        this.handleSave()
        return
      }

      // 检测 "/" 快捷键打开插入菜单
      if (event.key === '/' && !event.ctrlKey && !event.metaKey && !event.altKey) {
        // 检查当前是否在空内容的段落中
        if (this.editor && this.editor.view.hasFocus()) {
          const { state } = this.editor
          const { selection } = state
          const $pos = selection.$anchor
          const node = $pos.parent

          // 检查是否在空段落、空列表项等空内容元素中
          const isEmpty = (
            (node.type.name === 'paragraph' && node.content.size === 0) ||
            (node.type.name === 'listItem' && node.content.size === 1 && node.firstChild?.content.size === 0)
          ) && selection.empty

          if (isEmpty) {
            // 不阻止 "/" 字符输入，让它正常显示
            // 延迟触发菜单，确保 "/" 字符先插入到编辑器中
            setTimeout(() => {
              this.triggerSlashCommand()
            }, 10)
          }
        }
      }
    },

    handleSave() {
      // 触发保存事件，传递当前内容
      const content = this.getContent()
      this.$emit('save', content)

      // 如果有自定义保存处理函数，调用它
      if (this.onSave && typeof this.onSave === 'function') {
        this.onSave(content)
      }
    },

    triggerSlashCommand() {
      // 通过 $refs 访问 InsertMenu 组件并触发显示
      const insertMenuComponent = this.$refs.insertMenu
      if (insertMenuComponent) {
        insertMenuComponent.forceShowMenu()
      }
    },

    checkForSlashKeyword() {
      // 只有当 InsertMenu 显示时才进行关键词检测
      if (!this.editor || !this.editor.view.hasFocus()) return
      if (!this.$refs.insertMenu || !this.$refs.insertMenu.showInsertMenu) return

      try {
        const { state } = this.editor
        const { selection } = state
        const node = selection.$anchor.parent

        // 获取当前段落的文本内容
        const textContent = node.textContent || ''

        // 查找最后一个斜杠的位置
        const lastSlashIndex = textContent.lastIndexOf('/')

        if (lastSlashIndex !== -1) {
          // 计算光标相对于段落开始的位置
          const cursorPos = selection.from - selection.$anchor.start()

          // 确保光标在斜杠之后
          if (cursorPos > lastSlashIndex) {
            // 提取斜杠后的关键词
            const keyword = textContent.substring(lastSlashIndex + 1, cursorPos)

            // 更新 InsertMenu 的搜索关键词
            if (this.$refs.insertMenu.searchKeyword !== keyword) {
              this.$refs.insertMenu.searchKeyword = keyword
              this.$refs.insertMenu.filterItems()
            }
          }
        } else {
          // 如果没有找到斜杠，清除搜索关键词
          if (this.$refs.insertMenu.searchKeyword !== '') {
            this.$refs.insertMenu.searchKeyword = ''
            this.$refs.insertMenu.filterItems()
          }
        }
      } catch (error) {
        console.warn('checkForSlashKeyword error:', error)
      }
    },

    updateReplaceData() {
      if (!this.editor) return
      const selectedText = this.getSelectedText()
      const allContent = this.editor.getHTML()

      this.replaceData = {
        selected_content: selectedText,
        all_content: allContent
      }
    },

    getSelectedText() {
      if (!this.editor) return ''
      const { state } = this.editor
      const { selection } = state
      return state.doc.textBetween(selection.from, selection.to, ' ')
    },

    async handleAiRewrite() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要重写的内容')
        return
      }

      this.hideContextMenu()

      // 获取选中内容的位置
      const { from, to } = this.editor.state.selection

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('rewrite', selectedText)

      try {
        // 删除选中内容并插入AI占位符
        this.editor.chain().focus()
          .deleteRange({ from, to })
          .insertContent(placeholderHtml)
          .run()

        const response = await GetAiContent({
          prompt: `请重写以下内容，使其更加流畅和专业：\n\n${selectedText}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('AI 重写完成')
        }
      } catch (error) {
        // 错误时恢复原文
        this.removeAIPlaceholder(aiId, true)
        this.$message.error('AI 重写失败：' + error.message)
      }
    },

    async handleExpandParagraph() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要扩写的段落')
        return
      }

      this.hideContextMenu()

      // 获取选中内容的位置
      const { from, to } = this.editor.state.selection

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('expand', selectedText)

      try {
        // 删除选中内容并插入AI占位符
        this.editor.chain().focus()
          .deleteRange({ from, to })
          .insertContent(placeholderHtml)
          .run()

        const response = await GetAiContent({
          prompt: `请将以下段落进行扩写，增加更多细节和说明：\n\n${selectedText}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('段落扩写完成')
        }
      } catch (error) {
        // 错误时恢复原文
        this.removeAIPlaceholder(aiId, true)
        this.$message.error('段落扩写失败：' + error.message)
      }
    },

    async handleAIIntro() {
      this.hideContextMenu()
      const content = this.editor.getHTML()

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('start')

      try {
        // 在文档开头插入AI占位符
        this.editor.chain().focus('start').insertContent(placeholderHtml).run()

        const response = await GetAiContent({
          prompt: `基于以下内容，生成一个引人入胜的开头段落：\n\n${content}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 清理生成的内容
          let cleanContent = response.content
            .replace(/<p><br[^>]*><\/p>/g, '') // 移除空段落
            .replace(/<br\s*\/?>\s*<br\s*\/?>/g, '<br>') // 合并连续换行
            .replace(/^\s*<br[^>]*>\s*/g, '') // 移除开头的换行
            .replace(/\s*<br[^>]*>\s*$/g, '') // 移除结尾的换行
            .trim()

          // 确保内容以段落形式插入
          if (!cleanContent.startsWith('<p>')) {
            cleanContent = `<p>${cleanContent}</p>`
          }

          // 替换AI占位符
          this.replaceAIPlaceholder(aiId, cleanContent)
          this.$message.success('AI 开头生成完成')
        }
      } catch (error) {
        // 错误时移除AI占位符
        this.removeAIPlaceholder(aiId)
        this.$message.error('AI 开头生成失败：' + error.message)
      }
    },

    async handleAIEnding() {
      this.hideContextMenu()
      const content = this.editor.getHTML()

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('end')

      try {
        // 在文档结尾插入AI占位符
        this.editor.chain().focus('end').insertContent('<br>' + placeholderHtml).run()

        const response = await GetAiContent({
          prompt: `基于以下内容，生成一个恰当的结尾段落：\n\n${content}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 清理生成的内容
          let cleanContent = response.content
            .replace(/<p><br[^>]*><\/p>/g, '') // 移除空段落
            .replace(/<br\s*\/?>\s*<br\s*\/?>/g, '<br>') // 合并连续换行
            .replace(/^\s*<br[^>]*>\s*/g, '') // 移除开头的换行
            .replace(/\s*<br[^>]*>\s*$/g, '') // 移除结尾的换行
            .trim()

          if (!cleanContent.startsWith('<p>')) {
            cleanContent = `<p>${cleanContent}</p>`
          }

          this.replaceAIPlaceholder(aiId, cleanContent)
          this.$message.success('AI 结尾生成完成')
        }
      } catch (error) {
        // 错误时移除AI占位符
        this.removeAIPlaceholder(aiId)
        this.$message.error('AI 结尾生成失败：' + error.message)
      }
    },

    async handleFirstPersonRewrite() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要优化的内容')
        return
      }

      this.hideContextMenu()

      // 获取选中内容的位置
      const { from, to } = this.editor.state.selection

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('firstPerson', selectedText)

      try {
        // 删除选中内容并插入AI占位符
        this.editor.chain().focus()
          .deleteRange({ from, to })
          .insertContent(placeholderHtml)
          .run()

        const response = await GetAiContent({
          prompt: `请将以下内容改写为第一人称视角，增加个人经验和感受：\n\n${selectedText}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('第一人称优化完成')
        }
      } catch (error) {
        // 错误时恢复原文
        this.removeAIPlaceholder(aiId, true)
        this.$message.error('第一人称优化失败：' + error.message)
      }
    },

    async handleTechToExample() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要转换的技术内容')
        return
      }

      this.hideContextMenu()

      // 获取选中内容的位置
      const { from, to } = this.editor.state.selection

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('techToExample', selectedText)

      try {
        // 删除选中内容并插入AI占位符
        this.editor.chain().focus()
          .deleteRange({ from, to })
          .insertContent(placeholderHtml)
          .run()

        const response = await GetAiContent({
          prompt: `请将以下技术内容转换为具体的应用案例，包含实际场景和应用效果：\n\n${selectedText}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('技术转案例完成')
        }
      } catch (error) {
        // 错误时恢复原文
        this.removeAIPlaceholder(aiId, true)
        this.$message.error('技术转案例失败：' + error.message)
      }
    },

    async handleTranslateToTable() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要转换的内容')
        return
      }

      this.hideContextMenu()

      // 获取选中内容的位置
      const { from, to } = this.editor.state.selection

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('table', selectedText)

      try {
        // 删除选中内容并插入AI占位符
        this.editor.chain().focus()
          .deleteRange({ from, to })
          .insertContent(placeholderHtml)
          .run()

        const response = await GetAiContent({
          prompt: `请将以下内容整理成表格格式，使用HTML table标签：\n\n${selectedText}`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('转换表格完成')
        }
      } catch (error) {
        // 错误时恢复原文
        this.removeAIPlaceholder(aiId, true)
        this.$message.error('转换表格失败：' + error.message)
      }
    },

    async handleInsertExpertOpinion() {
      this.hideContextMenu()

      // 获取当前文档内容用于上下文
      const content = this.editor.getHTML()
      const selectedText = this.getSelectedText()

      // 如果有选中文本，基于选中内容生成专家意见；否则基于全文
      const contextContent = selectedText || content

      if (!contextContent || contextContent.trim() === '') {
        this.$message.warning('请先输入一些内容，AI需要基于内容生成专家意见')
        return
      }

      // 创建AI占位符
      const { aiId, placeholderHtml } = this.createAIPlaceholder('expertOpinion')

      try {
        // 直接在当前位置插入AI占位符
        this.editor.chain().focus().insertContent(placeholderHtml).run()

        const response = await GetAiContent({
          prompt: `基于以下内容，请以专家的角度提供一个权威性的观点或意见。要求：
1. 专业性强，体现专家水平
2. 观点明确，有说服力
3. 与内容主题相关
4. 语言简洁有力，150字以内
5. 以引用块格式呈现

内容：
${contextContent}

请生成一个专家观点，格式如：
<blockquote><p><strong>专家观点：</strong>[您的专家意见内容]</p></blockquote>`,
          engine: 'gpt-4o-mini'
        })

        if (response && response.content) {
          // 替换AI占位符为生成的内容
          this.replaceAIPlaceholder(aiId, response.content)
          this.$message.success('AI 专家意见生成完成')
        }
      } catch (error) {
        // 错误时移除AI占位符
        this.removeAIPlaceholder(aiId)
        this.$message.error('AI 专家意见生成失败：' + error.message)
      }
    },

    handleGoogleImageSearch() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要搜索的文本')
        return
      }

      this.hideContextMenu()
      const searchUrl = `https://www.google.com/search?tbm=isch&q=${encodeURIComponent(selectedText)}`
      window.open(searchUrl, '_blank')
    },

    handleTranslateContent() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要翻译的内容')
        return
      }

      this.hideContextMenu()
      this.translateForm.original = selectedText
      this.translateForm.translated = ''
      this.translateVisible = true
    },

    async doTranslate() {
      if (!this.translateForm.targetLang || !this.translateForm.original) return

      this.translating = true

      try {
        const response = await GetAiContent({
          prompt: `请将以下内容翻译成${this.translateForm.targetLang}：\n\n${this.translateForm.original}`,
          engine: 'gpt-4o-mini'
        })

        if (response.data && response.data.content) {
          this.translateForm.translated = response.data.content
        }
      } catch (error) {
        this.$message.error('翻译失败：' + error.message)
      } finally {
        this.translating = false
      }
    },

    insertTranslation() {
      if (this.translateForm.translated) {
        this.replaceSelectedText(this.translateForm.translated)
        this.translateVisible = false
        this.$message.success('翻译已插入')
      }
    },

    clearEmptyParagraphs() {
      this.hideContextMenu()
      if (!this.editor) return

      const { tr } = this.editor.state
      let modified = false

      this.editor.state.doc.descendants((node, pos) => {
        if (node.type.name === 'paragraph' && node.content.size === 0) {
          tr.delete(pos, pos + node.nodeSize)
          modified = true
        }
      })

      if (modified) {
        this.editor.view.dispatch(tr)
        this.$message.success('空行清理完成')
      } else {
        this.$message.info('未发现空行')
      }
    },

    async createAIImage() {
      this.hideContextMenu()

      // 记录当前光标位置
      const currentPosition = this.editor.state.selection.from

      // 获取上下文内容（当前段落前后的文本）
      const doc = this.editor.state.doc
      const contextRange = 500 // 获取前后500字符作为上下文
      const startPos = Math.max(0, currentPosition - contextRange)
      const endPos = Math.min(doc.content.size, currentPosition + contextRange)
      const contextText = doc.textBetween(startPos, endPos)

      try {
        this.$message.info('正在分析上下文并生成图片描述...')

        // 第一步：分析上下文，生成图片描述
        const descriptionResponse = await GetAiContent({
          prompt: `请分析以下文本内容，为插入位置生成一个合适的图片描述。要求：
1. 图片描述应该与上下文内容高度相关
2. 描述要具体、生动，适合作为图片生成的提示词
3. 图片风格要专业、现代，适合商业文章使用
4. 只返回图片描述，不要其他解释

上下文内容：
${contextText}`,
          engine: 'gpt-4o',
          aiprompt_id: '',
          keywords: []
        })

        if (!descriptionResponse.original_content) {
          throw new Error('无法生成图片描述')
        }

        this.$message.info('正在生成图片...')

        // 第二步：使用图片描述生成图片
        const imageResponse = await GenerateImage({
          prompt: `Generate a professional, modern image that illustrates: ${descriptionResponse.original_content}. Style: high quality, clean, business-appropriate.`
        })

        // 检查图片生成结果
        if (!imageResponse.success || !imageResponse.images || imageResponse.images.length === 0) {
          throw new Error(imageResponse.msg || '图片生成失败，未收到图片数据')
        }

        // 获取第一张生成的图片
        const firstImage = imageResponse.images[0]
        if (!firstImage) {
          throw new Error('图片生成失败，未收到图片数据')
        }

        // 如果后端已经自动保存了图片，直接使用file_url
        if (firstImage.file_url) {
          // 第三步：直接使用已保存的图片URL插入到编辑器
          this.editor.chain()
            .focus()
            .setTextSelection(currentPosition)
            .setImage({
              src: firstImage.file_url,
              alt: descriptionResponse.original_content,
              title: '由AI生成的插图'
            })
            .run()

          this.$message.success('AI插图生成并插入成功！')
        } else {
          // 如果没有file_url，则需要手动上传base64数据
          this.$message.info('正在上传图片到云存储...')

          const uploadResponse = await UploadBase64Image({
            file_data: firstImage.url,
            directory_id: -1,
            keyword: descriptionResponse.original_content.substring(0, 50)
          })

          if (!uploadResponse.data || !uploadResponse.data.url) {
            throw new Error('图片上传失败')
          }

          // 第四步：在记录的位置插入图片
          this.editor.chain()
            .focus()
            .setTextSelection(currentPosition)
            .setImage({
              src: uploadResponse.data.url,
              alt: descriptionResponse.original_content,
              title: '由AI生成的插图'
            })
            .run()

          this.$message.success('AI插图生成并插入成功！')
        }
      } catch (error) {
        console.error('AI插图生成失败:', error)
        this.$message.error(`AI插图生成失败: ${error.message}`)
      }
    },

    async handleContentQualityCheck() {
      this.hideContextMenu()
      const content = this.editor.getHTML()

      try {
        this.$message.info('正在进行EEAT质量检查...')
        const response = await GetAiContent({
          aiprompt_id: '272',
          prompt_name: 'EEAT_CHECK',
          humanway: false,
          kvs: [{ 'content': content, 'title': this.title }]
        })

        if (response.data) {
          this.analysisResults = []
          this.analysisHtml = response.data.content || ''
          this.analysisVisible = true
        }
      } catch (error) {
        this.$message.error('质量检查失败：' + error.message)
      }
    },

    async handleDuplicateCheck() {
      this.hideContextMenu()
      const content = this.editor.getHTML()

      try {
        this.$message.info('正在检测重复内容...')
        const response = await GetAiContent({
          prompt: `请检测以下内容中是否存在重复或相似的段落，并提供修改建议：\n\n${content}`,
          engine: 'gpt-4o-mini'
        })

        if (response.data) {
          this.analysisResults = response.data.duplicates || []
          this.analysisHtml = response.data.content || ''
          this.analysisVisible = true
        }
      } catch (error) {
        this.$message.error('重复内容检测失败：' + error.message)
      }
    },

    handleAIImageSearch() {
      const selectedText = this.getSelectedText()
      if (!selectedText) {
        this.$message.warning('请先选择要搜索的文本')
        return
      }
      this.hideContextMenu()
      this.$message.info('AI 搜图功能开发中...')
    },

    replaceSelectedText(newText) {
      if (!this.editor) return

      const { state } = this.editor
      const { selection } = state

      if (selection.empty) {
        this.editor.chain().focus().insertContent(newText).run()
      } else {
        this.editor.chain().focus().deleteSelection().insertContent(newText).run()
      }
    },

    handleAISuccess(content) {
      if (content) {
        this.editor.chain().focus().insertContent(content).run()
      }
    },

    addLink() {
      // 检查当前选中内容是否已经是链接
      if (this.editor.isActive('link')) {
        // 编辑现有链接
        const linkAttrs = this.editor.getAttributes('link')
        this.currentLinkData = linkAttrs
      } else {
        // 创建新链接
        this.currentLinkData = {}
      }

      this.linkDialogVisible = true
    },

    editCurrentLink() {
      // 编辑当前光标位置的链接
      if (this.editor.isActive('link')) {
        const linkAttrs = this.editor.getAttributes('link')
        this.currentLinkData = linkAttrs
        this.linkDialogVisible = true
      } else {
        this.$message.warning('当前位置没有链接')
      }
    },

    removeLink() {
      if (!this.editor) return
      this.editor.chain().focus().unsetLink().run()
    },

    openImageDialog() {
      // 调用 InsertMenu 的图片对话框
      if (this.$refs.insertMenu && this.$refs.insertMenu.showImageDialog) {
        this.$refs.insertMenu.showImageDialog()
      }
    },

    handleImageButtonClick() {
      // 如果选中了图片，打开图片设置；否则打开插入图片对话框
      if (this.isImageSelected) {
        this.toggleImagePopup()
      } else {
        this.openImageDialog()
      }
    },

    addImage() {
      const url = window.prompt('请输入图片地址:')
      if (url) {
        this.editor.chain().focus().setImage({ src: url }).run()
      }
    },

    getContent() {
      return this.editor ? this.editor.getHTML() : ''
    },

    setContent(content) {
      if (this.editor) {
        this.editor.commands.setContent(content, false)
      }
    },

    focus() {
      if (this.editor) {
        this.editor.commands.focus()
      }
    },

    getCount() {
      if (!this.editor) return 0
      const content = this.editor.getText()
      return content.replace(/\s+/g, '').length
    },

    // 新增的富文本功能方法
    updateCurrentHeading() {
      if (!this.editor) return

      for (let level = 1; level <= 6; level++) {
        if (this.editor.isActive('heading', { level })) {
          this.currentHeading = level.toString()
          return
        }
      }
      this.currentHeading = 'paragraph'
    },

    setHeading(value, event) {
      if (!this.editor) return

      // 防止事件冒泡
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }

      if (value === 'paragraph') {
        this.editor.chain().focus().setParagraph().run()
      } else {
        const level = parseInt(value)
        this.editor.chain().focus().toggleHeading({ level }).run()
      }

      // 关闭格式弹窗
      this.hideFormatPopup()
    },

    insertTable() {
      if (!this.editor) return

      this.editor.chain().focus().insertTable({
        rows: 3,
        cols: 3,
        withHeaderRow: true
      }).run()
    },

    // HTML格式化函数
    formatHTML(html) {
      if (!html) return ''

      // 移除多余的空白字符
      html = html.replace(/\s+/g, ' ').trim()

      // 定义自闭合标签
      const voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']

      let formatted = ''
      let indent = 0
      const indentSize = 2
      let inTag = false
      let tagName = ''

      for (let i = 0; i < html.length; i++) {
        const char = html[i]
        const nextChar = html[i + 1]

        if (char === '<') {
          // 开始标签
          if (nextChar === '/') {
            // 结束标签
            indent -= indentSize
            if (formatted.slice(-1) !== '\n') {
              formatted += '\n' + ' '.repeat(Math.max(0, indent))
            } else {
              formatted += ' '.repeat(Math.max(0, indent))
            }
          } else {
            // 开始标签或自闭合标签
            if (formatted && formatted.slice(-1) !== '\n' && formatted.slice(-1) !== ' ') {
              formatted += '\n' + ' '.repeat(indent)
            } else if (formatted.slice(-1) === ' ') {
              formatted = formatted.slice(0, -1) + '\n' + ' '.repeat(indent)
            } else if (formatted) {
              formatted += ' '.repeat(indent)
            }
          }
          inTag = true
          tagName = ''
        } else if (char === '>') {
          // 标签结束
          formatted += char
          inTag = false

          // 检查是否是自闭合标签
          const isVoidElement = voidElements.includes(tagName.toLowerCase())
          const isSelfClosing = html.substring(i - 1, i + 1) === '/>'

          if (!isVoidElement && !isSelfClosing && !tagName.startsWith('/')) {
            indent += indentSize
          }

          continue
        } else if (inTag && char !== ' ') {
          // 收集标签名
          if (char.match(/[a-zA-Z\/]/)) {
            tagName += char
          }
        }

        formatted += char
      }

      // 清理多余的空行
      return formatted.replace(/\n\s*\n/g, '\n').trim()
    },

    toggleSourceMode() {
      if (!this.editor) return

      if (this.isSourceMode) {
        // 从源码模式切换到可视化模式
        this.editor.commands.setContent(this.sourceCode, false)
        this.isSourceMode = false
      } else {
        // 从可视化模式切换到源码模式
        const rawHTML = this.editor.getHTML()
        this.sourceCode = this.formatHTML(rawHTML)
        this.isSourceMode = true
      }
    },

    toggleFloatingToolbar() {
      this.enableFloatingToolbar = !this.enableFloatingToolbar
      this.$message.info(this.enableFloatingToolbar ? '已显示浮动工具栏' : '已隐藏浮动工具栏')
    },

    // 清理所有内联样式
    clearInlineStyles() {
      if (!this.editor) {
        this.$message.warning('编辑器未就绪')
        return
      }

      try {
        // 获取当前HTML内容
        const html = this.editor.getHTML()

        // 使用DOM解析和清理
        const tempDiv = document.createElement('div')
        tempDiv.innerHTML = html

        // 递归清理所有元素的style属性
        const removeStyles = (element) => {
          // 移除style属性
          if (element.hasAttribute('style')) {
            element.removeAttribute('style')
          }

          // 递归处理子元素
          Array.from(element.children).forEach(child => {
            removeStyles(child)
          })
        }

        removeStyles(tempDiv)

        // 获取清理后的HTML
        const cleanedHtml = tempDiv.innerHTML

        // 统计移除的样式数量
        const originalStyleCount = (html.match(/style="/g) || []).length
        const cleanedStyleCount = (cleanedHtml.match(/style="/g) || []).length
        const removedCount = originalStyleCount - cleanedStyleCount

        // 更新编辑器内容
        this.editor.commands.setContent(cleanedHtml)

        if (removedCount > 0) {
          this.$message.success(`✓ 已清理 ${removedCount} 个内联样式`)
        } else {
          this.$message.info('未发现内联样式')
        }

        console.log('清理内联样式完成:', {
          originalStyleCount,
          cleanedStyleCount,
          removedCount
        })
      } catch (error) {
        console.error('清理内联样式失败:', error)
        this.$message.error('清理失败: ' + error.message)
      }
    },

    onSourceCodeChange() {
      // 源码变化时的处理
      this.$emit('input', this.sourceCode)
    },

    toggleFullscreen() {
      this.isFullscreen = !this.isFullscreen

      if (this.isFullscreen) {
        document.body.style.overflow = 'hidden'
      } else {
        document.body.style.overflow = ''
      }
    },

    setFontSize(fontSize) {
      if (!this.editor) return
      // 获取当前选中的文本
      const selectedText = this.getSelectedText()
      if (selectedText) {
        // 如果有选中文本，替换为带样式的span
        const styledText = `<span style="font-size: ${fontSize}">${selectedText}</span>`
        this.editor.chain().focus().deleteSelection().insertContent(styledText).run()
      } else {
        // 如果没有选中文本，在当前位置插入带样式的空span作为输入框
        const placeholder = `<span style="font-size: ${fontSize}">请输入文字</span>`
        this.editor.chain().focus().insertContent(placeholder).run()
      }
    },

    setTextColor() {
      if (!this.editor) return
      this.editor.chain().focus().setColor(this.currentTextColor).run()
    },

    showMoreOptions() {
      // 实现显示更多选项的逻辑 - 可以显示更多格式化选项
      this.$message.info('更多选项功能开发中...')
    },

    // 浮动工具栏相关方法
    updateFloatingToolbar() {
      if (!this.editor || this.isSourceMode) {
        this.hideFloatingToolbar()
        return
      }

      const { state, view } = this.editor
      const { selection } = state

      // 如果格式弹窗或表格弹窗正在显示，不进行工具栏更新操作
      if (this.showFormatPopup || this.formatPopupActive || this.showTablePopup || this.tablePopupActive) {
        return
      }

      // 清除之前的定时器
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }

      // 检查当前光标是否在空段落中
      const $pos = selection.$anchor
      const node = $pos.parent
      const isEmpty = node.type.name === 'paragraph' &&
                     node.content.size === 0 &&
                     selection.empty

      // 检查是否在表格中
      const isInTable = this.editor.isActive('table')

      // 检查是否选中了图片
      const isImageSelected = this.editor.isActive('image')

      // 显示浮动工具栏的条件：
      // 1. 有选中内容，或
      // 2. 编辑器有焦点且不是空行，或
      // 3. 在表格中（表格中总是显示工具栏，因为需要表格操作按钮），或
      // 4. 选中了图片
      if (!selection.empty || (view.hasFocus() && !isEmpty) || (view.hasFocus() && isInTable) || isImageSelected) {
        this.showFloatingToolbar = true
        this.calculateToolbarPosition()
      } else {
        this.scheduleHideToolbar()
      }
    },

    // 检测工具栏是否会遮挡光标
    isToolbarBlockingCursor(toolbarRect, cursorRect, margin = 5) {
      return !(
        toolbarRect.bottom + margin < cursorRect.top ||
        toolbarRect.top - margin > cursorRect.bottom ||
        toolbarRect.right + margin < cursorRect.left ||
        toolbarRect.left - margin > cursorRect.right
      )
    },

    // 计算光标位置矩形
    getCursorRect(view, pos, containerRect) {
      const coords = view.coordsAtPos(pos)
      const cursorHeight = 20 // 光标高度估算
      return {
        top: coords.top - containerRect.top,
        bottom: coords.top - containerRect.top + cursorHeight,
        left: coords.left - containerRect.left,
        right: coords.left - containerRect.left + 2 // 光标宽度
      }
    },

    // 智能计算工具栏位置，避免遮挡光标
    calculateSmartToolbarPosition(view, selection, containerRect, toolbarWidth, toolbarHeight, margin) {
      const cursorPos = selection.empty ? selection.from : selection.from
      const cursorRect = this.getCursorRect(view, cursorPos, containerRect)

      // 对于第一行，优先使用右侧和下方位置
      const isFirstLine = cursorRect.top < toolbarHeight + 40 // 更严格的第一行判断

      console.log('光标位置:', Math.round(cursorRect.top), '是否第一行:', isFirstLine)

      // 根据位置调整优先级
      const positions = isFirstLine ? [
        // 第一行：只使用右侧和下方
        () => {
          const coords = view.coordsAtPos(cursorPos)
          const x = coords.left - containerRect.left + 120
          const y = coords.top - containerRect.top
          return { x, y, position: 'right-first-line' }
        },
        () => {
          const coords = view.coordsAtPos(cursorPos)
          const x = Math.max(10, coords.left - containerRect.left - toolbarWidth / 2)
          const y = coords.top - containerRect.top + 40
          return { x, y, position: 'bottom-first-line' }
        }
      ] : [
        // 普通情况：上方优先
        () => {
          const coords = view.coordsAtPos(cursorPos)
          let x = coords.left - containerRect.left
          const y = coords.top - containerRect.top - toolbarHeight - margin

          // 水平边界检查
          x = Math.max(10, Math.min(x - toolbarWidth / 2, containerRect.width - toolbarWidth - 10))
          return { x, y, position: 'top' }
        },
        () => {
          const coords = view.coordsAtPos(cursorPos)
          const x = coords.left - containerRect.left + 120
          const y = Math.max(20, coords.top - containerRect.top)
          return { x, y, position: 'right' }
        },
        () => {
          const coords = view.coordsAtPos(cursorPos)
          let x = coords.left - containerRect.left
          const y = coords.top - containerRect.top + 40

          // 水平边界检查
          x = Math.max(10, Math.min(x - toolbarWidth / 2, containerRect.width - toolbarWidth - 10))
          return { x, y, position: 'bottom' }
        }
      ]

      // 尝试每个位置，找到第一个不遮挡光标且在容器内的位置
      for (const getPosition of positions) {
        const pos = getPosition()

        // 更严格的边界检查
        const minPadding = 10 // 最小边距
        const isInBounds = pos.x >= minPadding &&
                          pos.y >= minPadding &&
                          pos.x + toolbarWidth <= containerRect.width - minPadding &&
                          pos.y + toolbarHeight <= containerRect.height - minPadding

        if (isInBounds) {
          // 计算该位置的工具栏矩形
          const toolbarRect = {
            top: pos.y,
            bottom: pos.y + toolbarHeight,
            left: pos.x,
            right: pos.x + toolbarWidth
          }

          // 更严格的遮挡检测：增加安全边距
          const safeMargin = 20 // 增加安全边距到20px
          if (!this.isToolbarBlockingCursor(toolbarRect, cursorRect, safeMargin)) {
            console.log('选择位置:', pos.position, '坐标:', Math.round(pos.x), Math.round(pos.y))
            return pos
          } else {
            console.log('位置遮挡光标:', pos.position, '工具栏y:', Math.round(pos.y), '光标y:', Math.round(cursorRect.top))
          }
        } else {
          console.log('位置超出边界:', pos.position, '坐标:', Math.round(pos.x), Math.round(pos.y))
        }
      }

      // 如果所有位置都有问题，选择右侧位置（相对最安全）
      const coords = view.coordsAtPos(cursorPos)
      return {
        x: Math.min(coords.left - containerRect.left + 100, containerRect.width - toolbarWidth - 10),
        y: Math.max(10, coords.top - containerRect.top - toolbarHeight / 2),
        position: 'right-safe'
      }
    },

    calculateToolbarPosition() {
      if (!this.editor) return

      const { state, view } = this.editor
      const { selection } = state

      // 获取编辑器容器
      const editorContainer = this.$el.querySelector('.tiptap-content')
      if (!editorContainer) return

      const containerRect = editorContainer.getBoundingClientRect()
      const toolbarWidth = 280 // 工具栏的最小宽度
      const toolbarHeight = 50 // 工具栏的高度（单行布局，减小了）
      const margin = 45 // 增加距离元素顶部的边距到45px

      // 检查是否选中了图片
      const isImageSelected = this.editor.isActive('image')

      let position
      if (isImageSelected) {
        // 图片选中时，使用特殊的图片定位逻辑
        const { node } = selection
        if (node && node.type.name === 'image') {
          const pos = selection.from
          const domAtPos = view.domAtPos(pos)
          const imageElement = domAtPos.node.nodeType === Node.ELEMENT_NODE ? domAtPos.node : domAtPos.node.parentElement

          if (imageElement && imageElement.tagName === 'IMG') {
            const imageRect = imageElement.getBoundingClientRect()
            let x = imageRect.left + imageRect.width / 2 - containerRect.left - toolbarWidth / 2
            let y = imageRect.top - containerRect.top - toolbarHeight - margin

            // 水平边界检查
            x = Math.max(10, Math.min(x, containerRect.width - toolbarWidth - 10))

            // 如果上方空间不够，显示在图片下方
            if (y < 10) {
              y = imageRect.bottom - containerRect.top + margin
            }

            position = { x, y, position: 'image-top' }
          }
        }
      } else {
        // 对于文本内容，使用智能定位算法
        position = this.calculateSmartToolbarPosition(view, selection, containerRect, toolbarWidth, toolbarHeight, margin)
      }

      // 如果没有获得位置，使用默认安全位置（右侧）
      if (!position) {
        const coords = view.coordsAtPos(selection.from)
        position = {
          x: Math.min(coords.left - containerRect.left + 100, containerRect.width - toolbarWidth - 10),
          y: Math.max(10, coords.top - containerRect.top - toolbarHeight / 2),
          position: 'default-safe'
        }
      }

      // 应用计算出的位置 - 固定左侧60px,垂直位置在当前行上方
      const coords = view.coordsAtPos(selection.from)
      const fixedLeftPosition = 60 // 固定在左侧60px
      const toolbarOffsetTop = 60 // 工具栏在当前行上方60px,避免遮挡当前行
      const minTopPosition = 10 // 最小top位置(相对于容器)

      // 计算相对于容器的位置
      const relativeTop = coords.top - containerRect.top - toolbarOffsetTop

      // 确保工具栏不会超出容器顶部
      const finalTop = Math.max(minTopPosition, relativeTop)

      this.floatingToolbarStyle = {
        top: finalTop + 'px',
        left: fixedLeftPosition + 'px'
      }

      // 调试信息（可选，生产环境可删除)
      if (process.env.NODE_ENV === 'development') {
        console.log('工具栏定位:', `top: ${Math.round(finalTop)}, cursorTop: ${Math.round(coords.top - containerRect.top)}, offset: ${toolbarOffsetTop}`)
      }
    },

    // 查找块级元素（段落、标题等）
    findBlockElement(node) {
      let currentNode = node

      // 向上遍历DOM树，找到块级元素
      while (currentNode && currentNode !== document.body) {
        if (currentNode.nodeType === Node.ELEMENT_NODE) {
          const tagName = currentNode.tagName.toLowerCase()
          if (['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'blockquote', 'pre', 'li'].includes(tagName)) {
            return currentNode
          }
        }
        currentNode = currentNode.parentNode
      }

      return null
    },

    scheduleHideToolbar() {
      // 如果格式弹窗或表格弹窗正在显示，不隐藏工具栏
      if (this.showFormatPopup || this.formatPopupActive || this.showTablePopup || this.tablePopupActive) {
        return
      }

      // 延迟隐藏工具栏，给用户时间点击工具栏按钮
      this.toolbarTimeout = setTimeout(() => {
        // 检查鼠标是否在工具栏上，或者任何选择器/弹窗是否活跃
        const toolbar = this.$el.querySelector('.floating-toolbar')
        const formatPopup = this.$el.querySelector('.format-popup')

        const tablePopup = this.$el.querySelector('.table-popup')

        if ((toolbar && toolbar.matches(':hover')) ||
            (formatPopup && formatPopup.matches(':hover')) ||
            (tablePopup && tablePopup.matches(':hover')) ||
            this.fontSelectActive ||
            this.colorPickerActive ||
            this.highlightPickerActive ||
            this.formatPopupActive ||
            this.tablePopupActive ||
            this.showFormatPopup ||
            this.showTablePopup) {
          return // 如果鼠标在工具栏或弹窗上，或任何选择器活跃，不隐藏
        }
        this.hideFloatingToolbar()
        this.hideFormatPopup() // 同时隐藏格式弹窗
        this.hideTablePopup() // 同时隐藏表格弹窗
      }, 300)
    },

    hideFloatingToolbar() {
      this.showFloatingToolbar = false
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onToolbarMouseEnter() {
      // 鼠标进入工具栏时，清除隐藏定时器
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onToolbarMouseLeave() {
      // 鼠标离开工具栏时，检查编辑器是否有焦点或任何选择器是否活跃
      if (!this.editor || (!this.editor.view.hasFocus() && !this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive)) {
        this.scheduleHideToolbar()
      }
    },

    toggleFormatPopup(event) {
      // 防止事件冒泡
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }

      if (this.showFormatPopup) {
        this.hideFormatPopup()
      } else {
        // 立即清除所有可能的隐藏定时器
        if (this.toolbarTimeout) {
          clearTimeout(this.toolbarTimeout)
          this.toolbarTimeout = null
        }

        // 确保工具栏保持显示
        this.showFloatingToolbar = true
        this.formatPopupActive = true

        // 立即显示弹窗，不等待nextTick
        this.showFormatPopup = true
        this.calculateFormatPopupPosition()

        // 在nextTick中再次确保位置正确
        this.$nextTick(() => {
          this.calculateFormatPopupPosition()
        })
      }
    },

    hideFormatPopup() {
      this.showFormatPopup = false
      this.formatPopupActive = false
    },

    calculateFormatPopupPosition() {
      if (!this.editor) return

      // 优先查找浮动工具栏中的按钮,如果没有再查找顶部工具栏
      let formatBtn = this.$el.querySelector('.floating-toolbar .toolbar-format-btn')
      if (!formatBtn) {
        formatBtn = this.$el.querySelector('.ai-toolbar .format-btn')
      }
      if (!formatBtn) return

      const btnRect = formatBtn.getBoundingClientRect()

      // 使用viewport坐标(position: fixed),弹窗显示在按钮下方
      const x = btnRect.left
      const y = btnRect.bottom + 8 // 在按钮下方8px

      // 确保弹窗不会超出视口右边界
      const popupWidth = 280 // format-popup的min-width
      const viewportWidth = window.innerWidth
      const finalX = Math.min(x, viewportWidth - popupWidth - 20)

      this.formatPopupStyle = {
        top: y + 'px',
        left: finalX + 'px'
      }

      // 调试信息
      if (process.env.NODE_ENV === 'development') {
        const isFloating = formatBtn.classList.contains('toolbar-format-btn')
        console.log('格式弹窗定位:', isFloating ? '浮动工具栏' : '顶部工具栏', `位置: (${Math.round(finalX)}, ${Math.round(y)})`)
      }
    },

    toggleBulletList(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }
      this.hideContextMenu()
      this.editor.chain().focus().toggleBulletList().run()
      this.hideFormatPopup()
    },

    toggleOrderedList(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }
      this.hideContextMenu()
      this.editor.chain().focus().toggleOrderedList().run()
      this.hideFormatPopup()
    },

    toggleBlockquote(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }
      this.hideContextMenu()
      this.editor.chain().focus().toggleBlockquote().run()
      this.hideFormatPopup()
    },

    toggleCodeBlock(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }
      this.hideContextMenu()
      this.editor.chain().focus().toggleCodeBlock().run()
      this.hideFormatPopup()
    },

    toggleAlignment() {
      // 循环切换文本对齐方式：left -> center -> right -> left
      const alignments = ['left', 'center', 'right']
      const currentIndex = alignments.indexOf(this.currentAlignment)
      const nextIndex = (currentIndex + 1) % alignments.length
      this.currentAlignment = alignments[nextIndex]

      // 应用对齐样式到编辑器
      if (this.editor) {
        this.editor.chain().focus().setTextAlign(this.currentAlignment).run()
      }
    },

    getAlignmentTitle() {
      const titles = {
        left: '左对齐',
        center: '居中对齐',
        right: '右对齐'
      }
      return titles[this.currentAlignment] || '左对齐'
    },

    updateCurrentListType() {
      // 根据编辑器当前状态更新列表类型
      if (!this.editor) return

      if (this.editor.isActive('bulletList')) {
        this.currentListType = 'bulletList'
      } else if (this.editor.isActive('orderedList')) {
        this.currentListType = 'orderedList'
      } else {
        this.currentListType = 'paragraph'
      }
    },

    toggleList() {
      if (!this.editor) return

      // 循环切换：段落 -> 无序列表 -> 有序列表 -> 段落
      if (this.currentListType === 'paragraph') {
        // 切换到无序列表
        this.editor.chain().focus().toggleBulletList().run()
      } else if (this.currentListType === 'bulletList') {
        // 切换到有序列表
        this.editor.chain().focus().toggleBulletList().toggleOrderedList().run()
      } else if (this.currentListType === 'orderedList') {
        // 切换到段落
        this.editor.chain().focus().toggleOrderedList().run()
      }

      // 立即更新状态
      this.$nextTick(() => {
        this.updateCurrentListType()
      })
    },

    getListTitle() {
      const titles = {
        paragraph: '段落',
        bulletList: '无序列表',
        orderedList: '有序列表'
      }
      const nextType = this.getNextListType()
      return `${titles[this.currentListType]} (点击切换到${titles[nextType]})`
    },

    getNextListType() {
      if (this.currentListType === 'paragraph') return 'bulletList'
      if (this.currentListType === 'bulletList') return 'orderedList'
      return 'paragraph'
    },

    // 表格相关方法
    updateTableState() {
      if (!this.editor) return

      // 检查当前光标是否在表格中
      this.isInTable = this.editor.isActive('table')
    },

    updateCurrentElementType() {
      if (!this.editor) return

      // 按优先级检测当前元素类型
      if (this.editor.isActive('table')) {
        this.currentElementType = 'table'
      } else if (this.editor.isActive('codeBlock')) {
        this.currentElementType = 'codeBlock'
      } else if (this.editor.isActive('blockquote')) {
        this.currentElementType = 'blockquote'
      } else if (this.editor.isActive('bulletList')) {
        this.currentElementType = 'bulletList'
      } else if (this.editor.isActive('orderedList')) {
        this.currentElementType = 'orderedList'
      } else {
        // 检测标题级别
        for (let level = 1; level <= 6; level++) {
          if (this.editor.isActive('heading', { level })) {
            this.currentElementType = `heading${level}`
            return
          }
        }
        // 默认为段落
        this.currentElementType = 'paragraph'
      }

      // 更新图片状态
      this.updateImageState()
    },

    // 更新图片选中状态
    updateImageState() {
      if (!this.editor) return

      const { state } = this.editor
      const { selection } = state

      console.log('updateImageState 调用:', {
        selectionFrom: selection.from,
        selectionTo: selection.to,
        isEmpty: selection.empty,
        hasNode: !!selection.node
      })

      // 检查当前选中的是否是图片节点
      const { node } = selection
      const isNodeSelection = !selection.empty && !!node
      const isImageNode = isNodeSelection && node.type.name === 'image'

      this.isImageSelected = isImageNode

      if (isImageNode) {
        this.selectedImageUrl = node.attrs.src || ''
        this.selectedImageAlt = node.attrs.alt || ''
        this.selectedImageHref = node.attrs.href || ''
        this.selectedImageTarget = node.attrs.target || ''

        console.log('图片被选中:', {
          isImageSelected: this.isImageSelected,
          selectedImageUrl: this.selectedImageUrl,
          selectedImageAlt: this.selectedImageAlt,
          selectedImageHref: this.selectedImageHref,
          selectedImageTarget: this.selectedImageTarget,
          nodeType: node.type.name
        })
      } else {
        this.selectedImageUrl = ''
        this.selectedImageAlt = ''
        this.selectedImageHref = ''
        this.selectedImageTarget = ''

        console.log('图片未选中:', {
          isImageSelected: this.isImageSelected,
          selectionType: selection.empty ? 'empty' : (isNodeSelection ? 'node' : 'text'),
          nodeType: node ? node.type.name : 'none'
        })
      }
    },

    // 查找选中范围内的图片节点
    findImageNode(selection) {
      let imageNode = null
      const { state } = this.editor

      state.doc.nodesBetween(selection.from, selection.to, (node) => {
        if (node.type.name === 'image') {
          imageNode = node
          return false // 停止遍历
        }
      })

      return imageNode
    },

    getCurrentElementTitle() {
      // 如果选中了图片，返回图片标题
      if (this.isImageSelected) {
        return '图片'
      }

      const titles = {
        paragraph: '段落',
        heading1: '标题 1',
        heading2: '标题 2',
        heading3: '标题 3',
        heading4: '标题 4',
        heading5: '标题 5',
        heading6: '标题 6',
        bulletList: '无序列表',
        orderedList: '有序列表',
        blockquote: '引用',
        codeBlock: '代码块',
        table: '表格'
      }
      return titles[this.currentElementType] || '段落'
    },

    toggleTablePopup(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }

      if (this.showTablePopup) {
        this.hideTablePopup()
      } else {
        // 立即清除所有可能的隐藏定时器
        if (this.toolbarTimeout) {
          clearTimeout(this.toolbarTimeout)
          this.toolbarTimeout = null
        }

        // 确保工具栏保持显示
        this.showFloatingToolbar = true
        this.tablePopupActive = true

        // 立即显示弹窗
        this.showTablePopup = true
        this.calculateTablePopupPosition()

        // 在nextTick中再次确保位置正确
        this.$nextTick(() => {
          this.calculateTablePopupPosition()
        })
      }
    },

    hideTablePopup() {
      this.showTablePopup = false
      this.tablePopupActive = false
    },

    calculateTablePopupPosition() {
      if (!this.editor) return

      // 获取编辑器容器和表格按钮
      const editorContainer = this.$el.querySelector('.tiptap-content')
      const tableBtn = this.$el.querySelector('.table-icon')?.parentElement
      if (!editorContainer || !tableBtn) return

      const containerRect = editorContainer.getBoundingClientRect()
      const btnRect = tableBtn.getBoundingClientRect()

      // 计算弹窗位置（相对于编辑器容器）
      const x = btnRect.left - containerRect.left
      const y = btnRect.bottom - containerRect.top + 8 // 在按钮下方8px

      this.tablePopupStyle = {
        top: y + 'px',
        left: x + 'px'
      }
    },

    // 表格操作方法
    toggleHeaderRow() {
      if (!this.editor) return
      this.editor.chain().focus().toggleHeaderRow().run()
      this.hideTablePopup()
      this.$message.success('表头行状态已切换')
    },

    toggleHeaderColumn() {
      if (!this.editor) return
      this.editor.chain().focus().toggleHeaderColumn().run()
      this.hideTablePopup()
      this.$message.success('表头列状态已切换')
    },

    addRowBefore() {
      if (!this.editor) return
      this.editor.chain().focus().addRowBefore().run()
      this.hideTablePopup()
      this.$message.success('已在上方插入行')
    },

    addRowAfter() {
      if (!this.editor) return
      this.editor.chain().focus().addRowAfter().run()
      this.hideTablePopup()
      this.$message.success('已在下方插入行')
    },

    addColumnBefore() {
      if (!this.editor) return
      this.editor.chain().focus().addColumnBefore().run()
      this.hideTablePopup()
      this.$message.success('已在左侧插入列')
    },

    addColumnAfter() {
      if (!this.editor) return
      this.editor.chain().focus().addColumnAfter().run()
      this.hideTablePopup()
      this.$message.success('已在右侧插入列')
    },

    deleteRow() {
      if (!this.editor) return
      this.editor.chain().focus().deleteRow().run()
      this.hideTablePopup()
      this.$message.success('已删除当前行')
    },

    deleteColumn() {
      if (!this.editor) return
      this.editor.chain().focus().deleteColumn().run()
      this.hideTablePopup()
      this.$message.success('已删除当前列')
    },

    deleteTable() {
      if (!this.editor) return
      this.$confirm('确定要删除整个表格吗？', '警告', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.editor.chain().focus().deleteTable().run()
        this.hideTablePopup()
        this.$message.success('表格已删除')
      }).catch(() => {
        // 用户取消删除
      })
    },

    onTableButtonMouseEnter() {
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onTableButtonMouseLeave() {
      if (this.showTablePopup) {
        setTimeout(() => {
          const tablePopup = this.$el.querySelector('.table-popup')
          const tableBtn = this.$el.querySelector('.table-icon')?.parentElement
          if (!(tablePopup && tablePopup.matches(':hover')) &&
              !(tableBtn && tableBtn.matches(':hover'))) {
            this.hideTablePopup()
            if (!this.editor.view.hasFocus()) {
              this.scheduleHideToolbar()
            }
          }
        }, 150)
      }
    },

    onTablePopupMouseEnter() {
      this.tablePopupActive = true
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onTablePopupMouseLeave() {
      setTimeout(() => {
        const tableBtn = this.$el.querySelector('.table-icon')?.parentElement
        if (!(tableBtn && tableBtn.matches(':hover'))) {
          this.hideTablePopup()
          if (!this.editor.view.hasFocus() && !this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive) {
            this.scheduleHideToolbar()
          }
        }
      }, 200)
    },

    updateCurrentAlignment() {
      // 根据编辑器当前状态更新对齐方式
      if (!this.editor) return

      if (this.editor.isActive({ textAlign: 'center' })) {
        this.currentAlignment = 'center'
      } else if (this.editor.isActive({ textAlign: 'right' })) {
        this.currentAlignment = 'right'
      } else {
        this.currentAlignment = 'left'
      }
    },

    onFontSelectFocus() {
      // 字体选择器获得焦点时，阻止工具栏隐藏
      this.fontSelectActive = true
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onFontSelectBlur() {
      // 字体选择器失去焦点时，允许工具栏隐藏
      this.fontSelectActive = false
      // 延迟一点再检查是否需要隐藏工具栏
      setTimeout(() => {
        if (!this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive && !this.editor.view.hasFocus()) {
          this.scheduleHideToolbar()
        }
      }, 100)
    },

    onFontSelectVisibleChange(visible) {
      // 下拉菜单显示/隐藏时的处理
      this.fontSelectActive = visible
      if (visible) {
        // 下拉菜单打开时，清除隐藏定时器
        if (this.toolbarTimeout) {
          clearTimeout(this.toolbarTimeout)
          this.toolbarTimeout = null
        }
      } else {
        // 下拉菜单关闭时，检查是否需要隐藏工具栏
        setTimeout(() => {
          if (!this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive && !this.editor.view.hasFocus()) {
            this.scheduleHideToolbar()
          }
        }, 100)
      }
    },

    onColorPickerFocus() {
      // 颜色选择器获得焦点时，阻止工具栏隐藏
      this.colorPickerActive = true
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onColorPickerBlur() {
      // 颜色选择器失去焦点时，允许工具栏隐藏
      this.colorPickerActive = false
      // 延迟一点再检查是否需要隐藏工具栏
      setTimeout(() => {
        if (!this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive && !this.editor.view.hasFocus()) {
          this.scheduleHideToolbar()
        }
      }, 100)
    },

    setHighlightColor() {
      if (!this.editor) return
      this.editor.chain().focus().setHighlight({ color: this.currentHighlightColor }).run()
    },

    onHighlightPickerFocus() {
      // 背景颜色选择器获得焦点时，阻止工具栏隐藏
      this.highlightPickerActive = true
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onHighlightPickerBlur() {
      // 背景颜色选择器失去焦点时，允许工具栏隐藏
      this.highlightPickerActive = false
      // 延迟一点再检查是否需要隐藏工具栏
      setTimeout(() => {
        if (!this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive && !this.editor.view.hasFocus()) {
          this.scheduleHideToolbar()
        }
      }, 100)
    },

    // 处理粘贴事件
    handlePasteEvent(event) {
      const files = Array.from(event.clipboardData?.files || [])
      if (files.length > 0) {
        const imageFiles = files.filter(file => file.type.startsWith('image/'))
        if (imageFiles.length > 0) {
          event.preventDefault()
          event.stopPropagation()
          this.uploadImages(imageFiles)
          return true
        }
      }
      return false
    },

    // 处理拖拽事件
    handleDropEvent(event) {
      const files = Array.from(event.dataTransfer?.files || [])
      if (files.length > 0) {
        const imageFiles = files.filter(file => file.type.startsWith('image/'))
        if (imageFiles.length > 0) {
          event.preventDefault()
          event.stopPropagation()
          this.uploadImages(imageFiles)
          return true
        }
      }
      return false
    },

    // 上传图片文件
    uploadImages(files) {
      files.forEach((file, index) => {
        this.uploadSingleImage(file, index)
      })
    },

    // 上传单个图片
    uploadSingleImage(file, index = 0) {
      // 检查文件大小（限制10MB）
      const maxSize = 10 * 1024 * 1024 // 10MB
      if (file.size > maxSize) {
        this.$notify({
          title: '错误',
          message: '图片文件过大，请选择小于10MB的图片',
          type: 'error',
          duration: 3000
        })
        return
      }

      // 显示上传进度提示
      const uploadId = `upload-${Date.now()}-${index}`
      this.$notify({
        title: '上传中',
        message: `正在上传图片: ${file.name}`,
        type: 'info',
        duration: 0,
        customClass: `upload-notification-${uploadId}`
      })

      // 转换为base64
      const reader = new FileReader()
      reader.onload = () => {
        const base64Data = reader.result

        // 调用上传接口
        UploadBase64Image({
          file_data: base64Data,
          directory_id: -1,
          keyword: ''
        })
          .then(response => {
            // 关闭上传提示
            this.$notify.closeAll()

            // 显示成功提示
            this.$notify({
              title: '成功',
              message: `图片 ${file.name} 上传成功`,
              type: 'success',
              duration: 2000
            })

            // 插入图片到编辑器
            this.insertImageToEditor(response.data.url, file.name)
          })
          .catch(error => {
            // 关闭上传提示
            this.$notify.closeAll()

            // 显示错误提示
            this.$notify({
              title: '错误',
              message: `图片 ${file.name} 上传失败: ${error.message || '未知错误'}`,
              type: 'error',
              duration: 3000
            })
          })
      }

      reader.onerror = () => {
        this.$notify.closeAll()
        this.$notify({
          title: '错误',
          message: `无法读取图片文件: ${file.name}`,
          type: 'error',
          duration: 3000
        })
      }

      reader.readAsDataURL(file)
    },

    // 插入图片到编辑器
    insertImageToEditor(url, alt = '') {
      if (!this.editor) return

      this.editor.chain().focus().setImage({
        src: url,
        alt: alt
      }).run()
    },

    // 图片相关方法
    toggleImagePopup(event) {
      if (event) {
        event.preventDefault()
        event.stopPropagation()
      }

      if (this.showImagePopup) {
        this.hideImagePopup()
      } else {
        this.showImagePopup = true
        this.updateImagePopupPosition()
        // 阻止工具栏自动隐藏
        if (this.toolbarTimeout) {
          clearTimeout(this.toolbarTimeout)
          this.toolbarTimeout = null
        }
      }
    },

    // 更新图片弹窗位置
    updateImagePopupPosition() {
      this.$nextTick(() => {
        const button = this.$el.querySelector('button[title="图片设置"]')
        if (button) {
          const buttonRect = button.getBoundingClientRect()
          const containerRect = this.$el.getBoundingClientRect()

          this.imagePopupStyle = {
            top: (buttonRect.bottom - containerRect.top + 5) + 'px',
            left: (buttonRect.left - containerRect.left) + 'px'
          }
        }
      })
    },

    hideImagePopup() {
      this.showImagePopup = false
    },

    onImagePopupMouseEnter() {
      // 鼠标进入图片弹窗时，阻止工具栏隐藏
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onImagePopupMouseLeave() {
      // 鼠标离开图片弹窗时，检查是否需要隐藏
      setTimeout(() => {
        const imageBtn = this.$el.querySelector('.image-icon')?.parentElement
        if (!(imageBtn && imageBtn.matches(':hover'))) {
          this.hideImagePopup()
          if (!this.editor.view.hasFocus() && !this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive) {
            this.scheduleHideToolbar()
          }
        }
      }, 200)
    },

    // 预览图片
    previewImage() {
      if (!this.selectedImageUrl) return

      this.previewImageUrl = this.selectedImageUrl
      this.showImagePreviewDialog = true
    },

    // 编辑图片属性
    editImageProperties() {
      if (!this.selectedImageUrl) return

      this.imageEditForm.src = this.selectedImageUrl
      this.imageEditForm.alt = this.selectedImageAlt
      this.imageEditVisible = true
    },

    // 保存图片属性修改
    saveImageProperties() {
      if (!this.editor || !this.selectedImageUrl) return

      try {
        // 更新图片属性
        this.editor.chain().focus().updateAttributes('image', {
          src: this.imageEditForm.src,
          alt: this.imageEditForm.alt,
          title: this.imageEditForm.title,
          width: this.imageEditForm.width || undefined,
          height: this.imageEditForm.height || undefined
        }).run()

        this.imageEditVisible = false
        this.hideImagePopup()

        this.$notify({
          title: '成功',
          message: '图片属性已更新',
          type: 'success',
          duration: 2000
        })
      } catch (error) {
        console.error('Error updating image properties:', error)
        this.$notify({
          title: '错误',
          message: '更新图片属性失败',
          type: 'error',
          duration: 3000
        })
      }
    },

    // 删除图片
    deleteImage() {
      if (!this.editor || !this.isImageSelected) return

      this.editor.chain().focus().deleteSelection().run()
      this.hideImagePopup()

      this.$notify({
        title: '成功',
        message: '图片已删除',
        type: 'success',
        duration: 2000
      })
    },

    // 编辑图片URL
    editImageUrl() {
      if (!this.isImageSelected) return

      this.$prompt('请输入新的图片链接:', '编辑图片', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputPattern: /^https?:\/\/.+/,
        inputErrorMessage: '请输入有效的图片链接',
        inputValue: this.selectedImageUrl
      }).then(({ value }) => {
        this.editor.chain().focus().updateAttributes('image', {
          src: value
        }).run()
        this.hideImagePopup()
        this.$notify({
          title: '成功',
          message: '图片链接已更新',
          type: 'success',
          duration: 2000
        })
      }).catch(() => {
        // 用户取消操作
      })
    },

    // 图片左对齐
    alignImageLeft() {
      if (!this.editor || !this.isImageSelected) return

      this.editor.chain().focus().updateAttributes('image', {
        style: 'float: left; margin: 0 16px 8px 0;'
      }).run()
      this.hideImagePopup()

      this.$notify({
        title: '成功',
        message: '图片已左对齐',
        type: 'success',
        duration: 2000
      })
    },

    // 图片居中对齐
    alignImageCenter() {
      if (!this.editor || !this.isImageSelected) return

      this.editor.chain().focus().updateAttributes('image', {
        style: 'display: block; margin: 8px auto;'
      }).run()
      this.hideImagePopup()

      this.$notify({
        title: '成功',
        message: '图片已居中对齐',
        type: 'success',
        duration: 2000
      })
    },

    // 图片右对齐
    alignImageRight() {
      if (!this.editor || !this.isImageSelected) return

      this.editor.chain().focus().updateAttributes('image', {
        style: 'float: right; margin: 0 0 8px 16px;'
      }).run()
      this.hideImagePopup()

      this.$notify({
        title: '成功',
        message: '图片已右对齐',
        type: 'success',
        duration: 2000
      })
    },

    // 处理图片错误
    handleImageError() {
      this.$notify({
        title: '错误',
        message: '图片加载失败',
        type: 'error',
        duration: 3000
      })
    },

    // 下载图片
    downloadImage() {
      if (!this.selectedImageUrl) return

      const link = document.createElement('a')
      link.href = this.selectedImageUrl
      link.download = this.selectedImageAlt || 'image'
      link.target = '_blank'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    },

    // 添加或编辑图片链接
    toggleImageLink() {
      if (!this.editor || !this.isImageSelected) return

      const currentHref = this.selectedImageHref

      this.$prompt('请输入链接地址:', currentHref ? '编辑图片链接' : '添加图片链接', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputValue: currentHref || '',
        inputPlaceholder: 'https://example.com'
      }).then(({ value }) => {
        if (value && value.trim()) {
          // 询问是否在新窗口打开
          this.$confirm('是否在新窗口中打开链接?', '链接设置', {
            confirmButtonText: '新窗口',
            cancelButtonText: '当前窗口',
            distinguishCancelAndClose: true
          }).then(() => {
            // 新窗口打开
            this.setImageLink(value.trim(), '_blank')
          }).catch((action) => {
            if (action === 'cancel') {
              // 当前窗口打开
              this.setImageLink(value.trim(), '')
            }
          })
        }
      }).catch(() => {
        // 用户取消
      })
    },

    // 设置图片链接
    setImageLink(href, target) {
      if (!this.editor || !this.isImageSelected) return

      const { selection } = this.editor.state
      const node = this.editor.state.doc.nodeAt(selection.from)

      if (node && node.type.name === 'image') {
        const attrs = {
          ...node.attrs,
          href: href,
          target: target || null
        }

        this.editor.chain()
          .focus()
          .updateAttributes('image', attrs)
          .run()

        // 更新状态
        this.selectedImageHref = href
        this.selectedImageTarget = target || ''

        this.$notify({
          title: '成功',
          message: '图片链接已设置',
          type: 'success',
          duration: 2000
        })
      }
    },

    // 删除图片链接
    removeImageLink() {
      if (!this.editor || !this.isImageSelected || !this.selectedImageHref) return

      this.$confirm('确定要删除图片链接吗?', '删除链接', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const { selection } = this.editor.state
        const node = this.editor.state.doc.nodeAt(selection.from)

        if (node && node.type.name === 'image') {
          const attrs = {
            ...node.attrs,
            href: null,
            target: null
          }

          this.editor.chain()
            .focus()
            .updateAttributes('image', attrs)
            .run()

          // 更新状态
          this.selectedImageHref = ''
          this.selectedImageTarget = ''

          this.$notify({
            title: '成功',
            message: '图片链接已删除',
            type: 'success',
            duration: 2000
          })
        }
      }).catch(() => {
        // 用户取消
      })
    },

    // 图片按钮鼠标事件
    onImageButtonMouseEnter() {
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onImageButtonMouseLeave() {
      // 鼠标离开图片按钮时，检查是否需要隐藏弹窗和工具栏
      setTimeout(() => {
        const imagePopup = this.$el.querySelector('.image-popup')
        if (!(imagePopup && imagePopup.matches(':hover'))) {
          this.hideImagePopup()
          if (!this.editor.view.hasFocus() && !this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive && !this.formatPopupActive) {
            this.scheduleHideToolbar()
          }
        }
      }, 200)
    },

    onFormatPopupMouseEnter() {
      // 鼠标进入格式弹窗时，阻止工具栏隐藏
      this.formatPopupActive = true
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onFormatPopupMouseLeave() {
      // 鼠标离开格式弹窗时，检查是否需要隐藏
      // 不立即隐藏弹窗，给用户一些缓冲时间
      setTimeout(() => {
        // 检查鼠标是否在格式按钮上
        const formatBtn = this.$el.querySelector('.toolbar-format-btn')
        if (!(formatBtn && formatBtn.matches(':hover'))) {
          this.hideFormatPopup()
          // 如果编辑器也没有焦点，则隐藏整个工具栏
          if (!this.editor.view.hasFocus() && !this.fontSelectActive && !this.colorPickerActive && !this.highlightPickerActive) {
            this.scheduleHideToolbar()
          }
        }
      }, 200)
    },

    onFormatButtonMouseEnter() {
      // 鼠标进入格式按钮时，阻止工具栏隐藏
      if (this.toolbarTimeout) {
        clearTimeout(this.toolbarTimeout)
        this.toolbarTimeout = null
      }
    },

    onFormatButtonMouseLeave() {
      // 鼠标离开格式按钮时，检查是否需要隐藏弹窗
      if (this.showFormatPopup) {
        setTimeout(() => {
          // 检查鼠标是否在弹窗上或按钮上
          const formatPopup = this.$el.querySelector('.format-popup')
          const formatBtn = this.$el.querySelector('.toolbar-format-btn')
          if (!(formatPopup && formatPopup.matches(':hover')) &&
              !(formatBtn && formatBtn.matches(':hover'))) {
            this.hideFormatPopup()
            // 如果编辑器没有焦点，也隐藏工具栏
            if (!this.editor.view.hasFocus()) {
              this.scheduleHideToolbar()
            }
          }
        }, 150)
      }
    },

    // 拖拽相关方法
    startDrag(event) {
      event.preventDefault()
      this.isDragging = true

      // 获取鼠标或触摸位置
      const clientX = event.type === 'touchstart' ? event.touches[0].clientX : event.clientX
      const clientY = event.type === 'touchstart' ? event.touches[0].clientY : event.clientY

      this.dragStartX = clientX
      this.dragStartY = clientY

      // 获取当前工具栏位置
      const currentTop = parseInt(this.floatingToolbarStyle.top) || 0
      const currentLeft = parseInt(this.floatingToolbarStyle.left) || 0

      this.toolbarDragOffset = {
        x: currentLeft,
        y: currentTop
      }

      // 绑定全局事件
      document.addEventListener('mousemove', this.handleDrag)
      document.addEventListener('mouseup', this.endDrag)
      document.addEventListener('touchmove', this.handleDrag, { passive: false })
      document.addEventListener('touchend', this.endDrag)

      // 添加拖拽样式
      document.body.style.userSelect = 'none'
      document.body.style.cursor = 'grabbing'
    },

    handleDrag(event) {
      if (!this.isDragging) return

      event.preventDefault()

      // 获取鼠标或触摸位置
      const clientX = event.type === 'touchmove' ? event.touches[0].clientX : event.clientX
      const clientY = event.type === 'touchmove' ? event.touches[0].clientY : event.clientY

      // 计算移动距离
      const deltaX = clientX - this.dragStartX
      const deltaY = clientY - this.dragStartY

      // 计算新位置
      const newLeft = this.toolbarDragOffset.x + deltaX
      const newTop = this.toolbarDragOffset.y + deltaY

      // 获取编辑器容器边界，限制工具栏在容器内
      const editorContainer = this.$el.querySelector('.tiptap-content')
      if (editorContainer) {
        const containerRect = editorContainer.getBoundingClientRect()
        const toolbarWidth = 300 // 工具栏估计宽度
        const toolbarHeight = 50 // 工具栏估计高度

        // 限制边界
        const minLeft = 10
        const maxLeft = containerRect.width - toolbarWidth - 10
        const minTop = 10
        const maxTop = containerRect.height - toolbarHeight - 10

        this.floatingToolbarStyle = {
          top: Math.max(minTop, Math.min(maxTop, newTop)) + 'px',
          left: Math.max(minLeft, Math.min(maxLeft, newLeft)) + 'px'
        }
      } else {
        this.floatingToolbarStyle = {
          top: newTop + 'px',
          left: newLeft + 'px'
        }
      }
    },

    endDrag() {
      if (!this.isDragging) return

      this.isDragging = false

      // 移除全局事件
      document.removeEventListener('mousemove', this.handleDrag)
      document.removeEventListener('mouseup', this.endDrag)
      document.removeEventListener('touchmove', this.handleDrag)
      document.removeEventListener('touchend', this.endDrag)

      // 恢复样式
      document.body.style.userSelect = ''
      document.body.style.cursor = ''
    }

  }
}
</script>

<style scoped>
.ai-tiptap-editor {
  position: relative;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background-color: #fff;
  overflow: visible; /* 确保浮动元素不被裁剪 */
}

.ai-toolbar {
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid #e6ebf5;
  padding: 8px 12px;
  background-color: #f8f9fa;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.toolbar-group {
  display: flex;
  align-items: center;
  gap: 4px;
  border-right: 1px solid #e6ebf5;
  padding-right: 8px;
  margin-right: 4px;
}

.toolbar-group:last-child {
  border-right: none;
  padding-right: 0;
  margin-right: 0;
}

.toolbar-btn-small {
  padding: 4px 6px;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  color: #606266;
  transition: all 0.2s;
  min-width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.toolbar-btn-small:hover {
  background-color: #f0f0f0;
  border-color: #c0c4cc;
}

.toolbar-btn-small.is-active {
  background-color: #409eff;
  color: white;
  border-color: #409eff;
}

.toolbar-btn-small:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.current-element-icon-small {
  display: flex;
  align-items: center;
  gap: 2px;
  font-size: 10px;
}

.format-arrow-small {
  font-size: 8px;
  margin-left: 2px;
}

.alignment-icon-small {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.align-line-small {
  height: 1px;
  background-color: currentColor;
  transition: all 0.2s;
}

.align-line-small.align-left {
  width: 12px;
}

.align-line-small.align-center {
  width: 8px;
  margin: 0 auto;
}

.align-line-small.align-right {
  width: 12px;
  margin-left: auto;
}

.align-line-small.align-justify {
  width: 12px;
}

.list-toggle-icon-small {
  font-size: 10px;
}

.color-picker-wrapper-small {
  position: relative;
  display: inline-block;
}

.color-btn-small {
  position: relative;
  overflow: visible;
}

.color-icon-small {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 10px;
  font-weight: bold;
}

.color-bar-small {
  width: 12px;
  height: 2px;
  margin-top: 1px;
  border-radius: 1px;
}

.highlight-icon-small {
  font-size: 10px;
}

.highlight-bg-small {
  padding: 1px 3px;
  border-radius: 2px;
  font-weight: bold;
}

.color-input-small {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.font-size-select-small {
  font-size: 12px;
}

.font-size-select-small .el-input__inner {
  padding: 0 4px;
  height: 24px;
  line-height: 22px;
}

/* AI 标记样式 */
.ai-tiptap-editor >>> .ai-marker {
  background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
  border: 2px solid #409eff;
  border-radius: 12px;
  margin: 16px 0;
  padding: 16px;
  position: relative;
  overflow: hidden;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}

.ai-tiptap-editor >>> .ai-marker::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  animation: ai-shimmer 2s infinite;
}

.ai-tiptap-editor >>> .ai-marker-content {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  font-weight: 500;
  color: #1976d2;
  position: relative;
  z-index: 1;
}

.ai-tiptap-editor >>> .ai-icon {
  font-size: 20px;
  animation: ai-bounce 1.5s infinite;
}

.ai-tiptap-editor >>> .ai-text {
  flex: 1;
}

.ai-tiptap-editor >>> .ai-progress {
  width: 24px;
  height: 24px;
  border: 2px solid #e3f2fd;
  border-top: 2px solid #409eff;
  border-radius: 50%;
  animation: ai-spin 1s linear infinite;
}

/* 替换型AI标记 - 显示原内容 */
.ai-tiptap-editor >>> .ai-marker.ai-replace .ai-original-content {
  margin-top: 12px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.8);
  border-radius: 8px;
  border-left: 4px solid #ffc107;
  font-size: 13px;
  color: #666;
  line-height: 1.5;
}

.ai-tiptap-editor >>> .ai-marker.ai-replace .ai-original-content::before {
  content: "📝 原内容：";
  display: block;
  font-weight: 600;
  color: #f57c00;
  margin-bottom: 6px;
}

/* 插入型AI标记 - 特殊样式 */
.ai-tiptap-editor >>> .ai-marker.ai-insert {
  border-color: #67c23a;
  background: linear-gradient(135deg, #f0f9f0 0%, #e8f5e8 100%);
}

.ai-tiptap-editor >>> .ai-marker.ai-insert .ai-marker-content {
  color: #4caf50;
}

.ai-tiptap-editor >>> .ai-marker.ai-insert .ai-progress {
  border-top-color: #67c23a;
}

/* 动画效果 */
@keyframes ai-shimmer {
  0% { left: -100%; }
  100% { left: 100%; }
}

@keyframes ai-bounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-4px); }
  60% { transform: translateY(-2px); }
}

@keyframes ai-spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.floating-toolbar {
  position: absolute; /* 使用absolute定位,相对于父容器 */
  z-index: 9999; /* 提高z-index确保显示在最上层 */
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(10px);
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 8px 12px; /* 减小内边距，因为是单行 */
  display: flex;
  align-items: center; /* 恢复单行布局 */
  gap: 4px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
  overflow: visible;
  min-width: 200px; /* 减小最小宽度 */
  transition: box-shadow 0.2s ease;
}

.floating-toolbar.is-dragging {
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
  cursor: grabbing;
}

/* 拖拽手柄样式 */
.toolbar-drag-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 24px;
  cursor: grab;
  color: #909399;
  margin-right: 4px;
  border-radius: 2px;
  transition: all 0.2s ease;
  padding: 2px;
}

.toolbar-drag-handle:hover {
  background-color: rgba(64, 158, 255, 0.1);
  color: #409eff;
}

.toolbar-drag-handle:active {
  cursor: grabbing;
  background-color: rgba(64, 158, 255, 0.2);
}

.floating-toolbar::before {
  content: '';
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 12px;
  height: 6px;
  background: rgba(255, 255, 255, 0.95);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
}

.fade-enter-active, .fade-leave-active {
  transition: all 0.2s ease;
}

.fade-enter, .fade-leave-to {
  opacity: 0;
  transform: translateY(10px) scale(0.95);
}

/* 工具栏行样式 */
.toolbar-row {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-start;
}

.toolbar-compact {
  justify-content: flex-start;
}

/* 弹窗行样式 */
.popup-row {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-start;
}

.popup-row:first-child {
  justify-content: center;
}

/* 当前元素图标样式 */
.current-element-icon {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 20px;
  justify-content: center;
}

.format-arrow {
  font-size: 8px;
  color: currentColor;
  opacity: 0.7;
}

/* 段落图标 */
.element-paragraph {
  font-size: 12px;
  font-weight: bold;
}

/* 标题图标 */
.element-heading {
  font-size: 10px;
  font-weight: bold;
}

/* 无序列表图标 */
.element-bullet-list {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 14px;
  height: 10px;
}

.element-bullet-list .bullet-dot {
  width: 2px;
  height: 2px;
  background-color: currentColor;
  border-radius: 50%;
  flex-shrink: 0;
}

.element-bullet-list .bullet-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.element-bullet-list .bullet-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 有序列表图标 */
.element-ordered-list {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 14px;
  height: 10px;
}

.element-ordered-list .number-text {
  font-size: 8px;
  font-weight: bold;
  width: 4px;
  text-align: center;
  flex-shrink: 0;
}

.element-ordered-list .number-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.element-ordered-list .number-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 引用图标 */
.element-blockquote {
  font-size: 10px;
  font-weight: bold;
}

/* 代码块图标 */
.element-code-block {
  font-size: 8px;
  font-weight: bold;
}

/* 表格图标 */
.element-table {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 10px;
}

.element-table .table-grid {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
}

.element-table .table-row {
  display: flex;
  gap: 1px;
  width: 100%;
}

.element-table .table-cell {
  width: 4px;
  height: 3px;
  border: 1px solid currentColor;
  border-radius: 1px;
  background-color: transparent;
}

/* 菜单图标样式 */
.menu-icon {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 12px;
  height: 10px;
}

.menu-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 文本对齐图标样式 */
.alignment-icon {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 14px;
  height: 10px;
}

.align-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
  transition: width 0.2s ease;
}

/* 左对齐样式 */
.align-line.align-left:nth-child(1) { width: 100%; }
.align-line.align-left:nth-child(2) { width: 80%; }
.align-line.align-left:nth-child(3) { width: 60%; }

/* 居中对齐样式 */
.align-line.align-center:nth-child(1) { width: 90%; margin: 0 auto; }
.align-line.align-center:nth-child(2) { width: 100%; margin: 0 auto; }
.align-line.align-center:nth-child(3) { width: 70%; margin: 0 auto; }

/* 右对齐样式 */
.align-line.align-right:nth-child(1) { width: 60%; margin-left: auto; }
.align-line.align-right:nth-child(2) { width: 80%; margin-left: auto; }
.align-line.align-right:nth-child(3) { width: 100%; margin-left: auto; }

/* 列表切换按钮图标样式 */
.list-toggle-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 12px;
}

/* 段落图标样式 */
.paragraph-icon {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
}

.paragraph-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

.paragraph-line:nth-child(1) { width: 100%; }
.paragraph-line:nth-child(2) { width: 85%; }
.paragraph-line:nth-child(3) { width: 70%; }

/* 无序列表图标样式 */
.bullet-list-icon {
  display: flex;
  align-items: center;
  gap: 3px;
  width: 100%;
}

.bullet-list-icon .bullet-dot {
  width: 3px;
  height: 3px;
  background-color: currentColor;
  border-radius: 50%;
  flex-shrink: 0;
}

.bullet-list-icon .bullet-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.bullet-list-icon .bullet-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 有序列表图标样式 */
.ordered-list-icon {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 100%;
}

.ordered-list-icon .number-text {
  font-size: 9px;
  font-weight: bold;
  width: 6px;
  text-align: center;
  flex-shrink: 0;
}

.ordered-list-icon .number-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.ordered-list-icon .number-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 表格按钮图标样式 */
.table-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 12px;
}

.table-grid {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
}

.table-row {
  display: flex;
  gap: 1px;
  width: 100%;
}

.table-cell {
  width: 6px;
  height: 4px;
  border: 1px solid currentColor;
  border-radius: 1px;
  background-color: transparent;
}

/* 表格操作弹窗样式 */
.table-popup {
  position: absolute;
  z-index: 1001;
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(12px);
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  overflow: visible;
  min-width: 320px;
}

/* 表格弹窗按钮样式 */
.popup-btn-wide {
  min-width: 140px !important;
  justify-content: flex-start !important;
  gap: 6px;
}

.popup-btn-full {
  width: 100% !important;
  justify-content: center !important;
  gap: 6px;
}

.popup-btn-danger {
  color: #f56c6c !important;
  border-color: #f56c6c !important;
}

.popup-btn-danger:hover {
  background-color: #fef0f0 !important;
  border-color: #f56c6c !important;
  color: #f56c6c !important;
}

.popup-btn-danger.is-active {
  background-color: #f56c6c !important;
  color: #ffffff !important;
  border-color: #f56c6c !important;
}

/* 颜色选择器样式 */
.color-picker-wrapper {
  position: relative;
  flex-shrink: 0;
}

.color-btn {
  position: relative;
}

.color-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-weight: bold;
  font-size: 12px;
}

.color-bar {
  width: 16px;
  height: 2px;
  border-radius: 1px;
  margin-top: 1px;
  border: 1px solid #e4e7ed;
}

/* 背景颜色按钮样式 */
.highlight-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  font-size: 12px;
}

.highlight-bg {
  width: 18px;
  height: 14px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #000;
  font-size: 10px;
  font-weight: bold;
  border: 1px solid #e4e7ed;
}

.color-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

/* 更多选项图标样式 */
.more-icon {
  font-size: 12px;
  color: currentColor;
}

/* 字体大小选择器样式 */
.floating-toolbar .font-size-select >>> .el-input__inner {
  height: 24px !important;
  line-height: 24px !important;
  padding: 0 20px 0 6px !important;
  font-size: 11px !important;
  border-radius: 4px !important;
  border: 1px solid #e4e7ed !important;
  background-color: rgba(255, 255, 255, 0.9) !important;
}

.floating-toolbar .font-size-select >>> .el-input__suffix {
  right: 2px !important;
}

.floating-toolbar .font-size-select >>> .el-input__suffix .el-select__caret {
  font-size: 10px !important;
}

.toolbar-divider {
  width: 1px;
  height: 24px;
  background: linear-gradient(to bottom, transparent, #e4e7ed 20%, #e4e7ed 80%, transparent);
  margin: 0 6px;
  flex-shrink: 0;
}

.toolbar-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px 6px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  background-color: #ffffff;
  color: #606266;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  min-width: 24px;
  height: 24px;
  white-space: nowrap;
  flex-shrink: 0;
}

.popup-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px 6px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  background-color: #ffffff;
  color: #606266;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  min-width: 24px;
  height: 24px;
  white-space: nowrap;
  flex-shrink: 0;
}

.toolbar-btn:hover,
.popup-btn:hover {
  background-color: #f0f9ff;
  color: #409eff;
  border-color: #b3d8ff;
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(64, 158, 255, 0.2);
}

.toolbar-btn.is-active,
.popup-btn.is-active {
  background-color: #409eff;
  color: #ffffff;
  border-color: #409eff;
  box-shadow: 0 2px 4px rgba(64, 158, 255, 0.4);
}

.toolbar-btn.is-active:hover,
.popup-btn.is-active:hover {
  background-color: #337ecc;
  border-color: #337ecc;
}

.toolbar-btn:disabled,
.popup-btn:disabled {
  background-color: #f9fafc;
  color: #c0c4cc;
  border-color: #e4e7ed;
  cursor: not-allowed;
  opacity: 0.6;
  transform: none;
  box-shadow: none;
}

.toolbar-btn:disabled:hover,
.popup-btn:disabled:hover {
  background-color: #f9fafc;
  color: #c0c4cc;
  border-color: #e4e7ed;
  transform: none;
  box-shadow: none;
}

.toolbar-group {
  display: flex;
  align-items: center;
  gap: 4px;
}

.toolbar-btn-small {
  padding: 2px 6px;
  border: 1px solid #e4e7ed;
  border-radius: 3px;
  background-color: #ffffff;
  color: #606266;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.2s ease;
  min-width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.toolbar-btn-small:hover {
  background-color: #f0f9ff;
  color: #409eff;
  border-color: #b3d8ff;
}

.toolbar-btn-small.is-active {
  background-color: #409eff;
  color: #ffffff;
  border-color: #409eff;
}

.toolbar-btn-small:disabled {
  background-color: #f9fafc;
  color: #c0c4cc;
  border-color: #e4e7ed;
  cursor: not-allowed;
  opacity: 0.6;
}

.tiptap-content {
  padding: 12px 0;
  max-width: 1080px;
  margin: 0 auto;
  overflow: visible; /* 确保浮动工具栏和弹窗不被裁剪 */
}

.tiptap-content.is-fullscreen {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2000;
  background: #fff;
  padding: 0;
}

.is-fullscreen .ai-tiptap-editor {
  height: 100vh;
  border: none;
  border-radius: 0;
}

.tiptap-content >>> .ProseMirror {
  outline: none;
  line-height: 1.6;
  min-height: 100px;
  padding: 0 20px;
  position: relative;
}

.tiptap-content >>> .ProseMirror p {
  margin: 0 0 8px 0;
}

.tiptap-content >>> .ProseMirror a {
  color: #409eff;
  text-decoration: underline;
}

.tiptap-content >>> .ProseMirror img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  margin: 8px 0;
}

.tiptap-content >>> .ProseMirror blockquote {
  border-left: 4px solid #dcdfe6;
  margin: 16px 0;
  padding-left: 16px;
  color: #606266;
}

.tiptap-content >>> .ProseMirror h1,
.tiptap-content >>> .ProseMirror h2,
.tiptap-content >>> .ProseMirror h3,
.tiptap-content >>> .ProseMirror h4,
.tiptap-content >>> .ProseMirror h5,
.tiptap-content >>> .ProseMirror h6 {
  margin: 16px 0 8px 0;
  font-weight: bold;
  line-height: 1.4;
}

.tiptap-content >>> .ProseMirror h1 { font-size: 2em; }
.tiptap-content >>> .ProseMirror h2 { font-size: 1.5em; }
.tiptap-content >>> .ProseMirror h3 { font-size: 1.25em; }
.tiptap-content >>> .ProseMirror h4 { font-size: 1.1em; }
.tiptap-content >>> .ProseMirror h5 { font-size: 1em; }
.tiptap-content >>> .ProseMirror h6 { font-size: 0.875em; }

.tiptap-content >>> .ProseMirror ul,
.tiptap-content >>> .ProseMirror ol {
  margin: 8px 0;
  padding-left: 24px;
}

.tiptap-content >>> .ProseMirror li {
  margin: 4px 0;
}

.tiptap-content >>> .ProseMirror code {
  background: #f5f7fa;
  color: #e6a23c;
  padding: 2px 4px;
  border-radius: 3px;
  font-size: 0.9em;
  font-family: 'Monaco', 'Consolas', monospace;
}

.tiptap-content >>> .ProseMirror pre {
  background: #f5f7fa;
  color: #303133;
  padding: 12px;
  border-radius: 6px;
  margin: 16px 0;
  overflow-x: auto;
  font-family: 'Monaco', 'Consolas', monospace;
}

.tiptap-content >>> .ProseMirror pre code {
  background: none;
  color: inherit;
  padding: 0;
  font-size: inherit;
}

.tiptap-content >>> .ProseMirror table {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
  margin: 16px 0;
  overflow: hidden;
}

.tiptap-content >>> .ProseMirror table td,
.tiptap-content >>> .ProseMirror table th {
  min-width: 1em;
  border: 1px solid #dcdfe6;
  padding: 8px 12px;
  vertical-align: top;
  box-sizing: border-box;
  position: relative;
}

.tiptap-content >>> .ProseMirror table th {
  background-color: #f5f7fa;
  font-weight: bold;
}

.tiptap-content >>> .ProseMirror hr {
  border: none;
  border-top: 2px solid #ebeef5;
  margin: 24px 0;
}

/* Placeholder 样式 */
.tiptap-content >>> .ProseMirror .is-editor-empty:first-child::before {
  content: attr(data-placeholder);
  float: left;
  color: #9ca3af;
  pointer-events: none;
  height: 0;
  font-style: italic;
}

.tiptap-content >>> .ProseMirror .is-empty::before {
  content: attr(data-placeholder);
  float: left;
  color: #9ca3af;
  pointer-events: none;
  height: 0;
  font-style: italic;
}

/* 源码模式样式 */
.tiptap-content >>> .el-textarea__inner {
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace !important;
  font-size: 14px !important;
  line-height: 1.5 !important;
  background-color: #1e1e1e !important;
  color: #d4d4d4 !important;
  border: 1px solid #3e3e42 !important;
  border-radius: 6px !important;
  padding: 16px !important;
  tab-size: 2 !important;
  white-space: pre !important;
  overflow-wrap: normal !important;
  overflow-x: auto !important;
}

.tiptap-content >>> .el-textarea__inner:focus {
  border-color: #007acc !important;
  box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2) !important;
}

.tiptap-content >>> .el-textarea__inner::placeholder {
  color: #6a6a6a !important;
  font-style: italic !important;
}

/* 右键菜单样式 */
.context-menu {
  position: fixed;
  background: #fff;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  z-index: 1000;
  min-width: 180px;
  max-height: 400px;
  overflow-y: auto;
}

.context-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  font-size: 14px;
  color: #606266;
  cursor: pointer;
  transition: background-color 0.2s;
}

.context-menu-item:hover {
  background-color: #f5f7fa;
  color: #409eff;
}

.context-menu-divider {
  height: 1px;
  background-color: #ebeef5;
  margin: 4px 0;
}

/* 格式选择弹窗样式 */
.format-popup {
  position: fixed; /* 使用fixed定位,相对于viewport,避免被任何容器遮挡 */
  z-index: 10000; /* 比floating-toolbar更高,确保弹窗在工具栏之上 */
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(12px);
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  overflow: visible;
  min-width: 280px;
}

/* 弹窗标题样式 */
.popup-title {
  font-size: 12px;
  color: #909399;
  text-align: center;
  margin-bottom: 4px;
  font-weight: 500;
}

/* 无序列表图标样式 */
.list-bullet-icon {
  display: flex;
  align-items: center;
  gap: 3px;
  width: 16px;
  height: 12px;
}

.bullet-dot {
  width: 3px;
  height: 3px;
  background-color: currentColor;
  border-radius: 50%;
  flex-shrink: 0;
}

.bullet-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.bullet-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 有序列表图标样式 */
.list-number-icon {
  display: flex;
  align-items: center;
  gap: 3px;
  width: 16px;
  height: 12px;
}

.number-text {
  font-size: 10px;
  font-weight: bold;
  width: 6px;
  text-align: center;
  flex-shrink: 0;
}

.number-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
}

.number-line {
  height: 1px;
  background-color: currentColor;
  border-radius: 1px;
}

/* 弹窗选择器样式 */
.format-popup .popup-select >>> .el-input__inner {
  height: 28px !important;
  line-height: 28px !important;
  padding: 0 20px 0 8px !important;
  font-size: 12px !important;
  border-radius: 4px !important;
  border: 1px solid #e4e7ed !important;
  background-color: rgba(255, 255, 255, 0.9) !important;
}

.format-popup .popup-select >>> .el-input__suffix {
  right: 4px !important;
}

.format-popup .popup-select >>> .el-input__suffix .el-select__caret {
  font-size: 12px !important;
}

/* 图片弹窗样式 */
.image-popup {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  min-width: 200px;
  background: white;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  z-index: 1000;
  padding: 8px;
}

/* 图片按钮样式 */
.image-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: transparent;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  color: #606266;
  white-space: nowrap;
  transition: all 0.2s ease;
}

.image-btn:hover {
  background: #f5f7fa;
  border-color: #409eff;
  color: #409eff;
}

.image-btn:active {
  background: #ecf5ff;
}

/* 图片弹窗按钮样式 */
.popup-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: transparent;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  color: #606266;
  white-space: nowrap;
  transition: all 0.2s ease;
  min-width: 0;
}

.popup-btn:hover {
  background: #f5f7fa;
  border-color: #409eff;
  color: #409eff;
}

.popup-btn:active {
  background: #ecf5ff;
}

.popup-btn-danger:hover {
  background: #fef0f0;
  border-color: #f56c6c;
  color: #f56c6c;
}

.popup-btn-danger:active {
  background: #fde2e2;
}

/* SVG 图标样式 */
.popup-btn svg {
  flex-shrink: 0;
}

.toolbar-btn svg {
  display: block;
}

/* 图片预览对话框样式 */
.image-preview-dialog >>> .el-dialog__body {
  text-align: center;
  padding: 20px;
}

.image-preview-dialog >>> .el-dialog__body img {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}

/* 图片编辑对话框样式 */
.image-edit-dialog >>> .el-form-item {
  margin-bottom: 20px;
}

.image-edit-dialog >>> .el-form-item__label {
  font-weight: 500;
}

/* 图片基础样式 */
.ProseMirror img.custom-image {
  max-width: 100%;
  height: auto;
  cursor: pointer;
  border-radius: 4px;
  transition: all 0.2s ease;
  margin: 0.5rem 0;
  display: block;
}

.ProseMirror img.custom-image:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* 图片选中状态样式 */
.ProseMirror img.custom-image.ProseMirror-selectednode {
  position: relative;
  outline: 2px solid #409eff !important;
  outline-offset: 2px !important;
  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.3) !important;
  border: 2px solid #409eff !important;
  border-radius: 6px !important;
}

/* 备用样式，确保边框显示 */
.tiptap-content >>> .ProseMirror img.ProseMirror-selectednode {
  outline: 3px solid #409eff !important;
  outline-offset: 1px !important;
  border: 2px solid #409eff !important;
  border-radius: 4px !important;
}

/* 全局强制样式 */
img.ProseMirror-selectednode {
  outline: 3px solid #409eff !important;
  outline-offset: 1px !important;
  box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.3) !important;
}

/* 带链接的图片样式 */
.ProseMirror a > img.custom-image {
  cursor: pointer;
  position: relative;
}

.ProseMirror a > img.custom-image:hover {
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
  border: 1px solid rgba(64, 158, 255, 0.3);
}

/* 带链接的图片在选中时的样式 */
.ProseMirror a > img.custom-image.ProseMirror-selectednode {
  outline: 3px solid #67c23a !important;
  outline-offset: 2px !important;
  box-shadow: 0 4px 16px rgba(103, 194, 58, 0.3) !important;
  border: 2px solid #67c23a !important;
}

/* 图片模式下的按钮样式 */
.toolbar-btn-image-active {
  background-color: #409eff !important;
  color: white !important;
  border-color: #409eff !important;
}

.toolbar-btn-image-active:hover {
  background-color: #66b3ff !important;
  border-color: #66b3ff !important;
}

.toolbar-btn-image-active:active {
  background-color: #3a8ee6 !important;
  border-color: #3a8ee6 !important;
}

/* 图片图标样式 */
.element-image {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
}

.element-image svg {
  color: currentColor;
}

/* AI 标记样式 */
.tiptap-content >>> .ai-marker {
  display: inline-block;
  position: relative;
  margin: 8px 0;
  padding: 12px 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 8px;
  border: 2px solid #409eff;
  overflow: hidden;
  animation: aiMarkerBounce 2s ease-in-out infinite;
  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.3);
}

.tiptap-content >>> .ai-marker::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  animation: aiMarkerShimmer 1.5s ease-in-out infinite;
}

.tiptap-content >>> .ai-marker-content {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  color: white;
  font-weight: 500;
  font-size: 14px;
}

.tiptap-content >>> .ai-marker .ai-icon {
  font-size: 16px;
  animation: aiIconSpin 2s linear infinite;
}

.tiptap-content >>> .ai-marker .ai-text {
  flex: 1;
}

.tiptap-content >>> .ai-marker .ai-progress {
  width: 20px;
  height: 20px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top: 2px solid white;
  border-radius: 50%;
  animation: aiProgressSpin 1s linear infinite;
}

/* 插入型AI标记样式 */
.tiptap-content >>> .ai-marker.ai-insert {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-color: #667eea;
}

/* 替换型AI标记样式 */
.tiptap-content >>> .ai-marker.ai-replace {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  border-color: #f5576c;
}

.tiptap-content >>> .ai-marker.ai-replace .ai-original-content {
  margin-top: 8px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  font-size: 12px;
  opacity: 0.8;
  border-left: 3px solid rgba(255, 255, 255, 0.5);
}

/* AI标记动画 */
@keyframes aiMarkerShimmer {
  0% { left: -100%; }
  100% { left: 100%; }
}

@keyframes aiMarkerBounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-3px); }
  60% { transform: translateY(-1px); }
}

@keyframes aiIconSpin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes aiProgressSpin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes aiPulse {
  0%, 100% { opacity: 0.8; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.05); }
}

.insert-menu {
  /* 这里是 InsertMenu.vue 的样式，需同步最大宽度 */
  max-width: 1080px !important;
  min-width: 450px;
}
</style>
